这篇文章基于前两篇文章:
Promise深入使用
then
返回结果总结
执行 then
方法会返回一些全新的 promise
实例p2
p2的状态和值是如何改变的?
- 不论执行的是基于
p1.then
存放的onfulfilledCallback/onrejectedCallback
两个方法中的哪一个 - 如果方法执行不报错
- 如果方法中返回一个全新的
Promise
实例,则“全新的Promise
实例”的成功和失败决定p2的成功和失败 - 如果不是返回
promise
,则[[PromiseState]]:fulfiled
,[[PromiseResult]]
:两个方法中任何一个的返回值
- 如果方法中返回一个全新的
- 如果方法执行报错:p2的
[[PromiseState]]:rejected
,[[PromiseResult]]:报错原因
let p1 = new Promise((resolve, reject) => {
resolve('OK');
//reject('NO');
});
let p2 = p1.then(result => {
console.log('P1成功-->', result);
return Promise.reject(10);
}, reason => {
console.log('P1失败-->', reason);
});
let p3 = p2.then(result => {
console.log('P2成功-->', result);
}, reason => {
console.log('P2失败-->', reason);
return Promise.resolve(10);
});
p3.then(result => {
console.log('P3成功-->', result);
}, reason => {
console.log('P3失败-->', reason);
return Promise.resolve(10);
});
console.log(1);
复制代码
将 resolve('OK');
注释结果:
promise链顺延/穿透总结
如果 onfulfilledCallback
/ onrejectedCallback
不传递,则状态和结果都会“顺延/穿透”到下一个同等状态应该执行的回调函数上(内部其实是自己补充了一些实现效果的默认函数)
new Promise((resolve, reject) => {
resolve('OK');
}).then(null, null)
.then(result => {
console.log('成功-->', result);
}, reason => {
console.log('失败-->', reason);
})
复制代码
相当于(内部会做处理):
new Promise((resolve, reject) => {
resolve('OK');
}).then(result=>result ,reason=>Promise.reject(reason) )
.then(result => {
console.log('成功-->', result);
}, reason => {
console.log('失败-->', reason);
})
复制代码
new Promise((resolve, reject) => {
resolve('OK');
// reject('NO');
}).then(null /*result=>result*/ , null /* reason=>Promise.reject(reason) */ )
.then(result => {
console.log('成功-->', result);
}, reason => {
console.log('失败-->', reason);
}).then(result => {
console.log('成功-->', result);
}, reason => {
console.log('失败-->', reason);
});
复制代码
resolve
打印:
reject
打印:
catch
只是 then
的语法糖,处理状态为失败下做的事情
Promise.prototype.catch = function (onrejectedCallback) {
return this.then(null, onrejectedCallback);
};
复制代码
new Promise((resolve, reject) => {
resolve('OK');
// reject('NO');
}).then(result => {
console.log('成功-->', result);
}).then(result => {
console.log('成功-->', result);
return Promise.reject('xx');
}).catch(reason => {
console.log('失败-->', reason);
});
复制代码
resolve
打印:
reject
打印:
跳过其他处理,直接到最后的 catch
new Promise((resolve, reject) => {
resolve('OK');
// reject('NO');
}).then(result => {
console.log('成功-->', result);
}).then(result => {
console.log('成功-->', result);
return Promise.reject('xx');
}).catch(reason => {
console.log('失败-->', reason);
});
复制代码
不会跳过
Promise.all
/ Promise.race
Promise.all 方法上一篇文章写了使用方法,这边举几个例子来理解使用
Promise.all([promise数组])
promise数组要求数组中的每一项尽可能都是promise实例。返回一个新的promise实例A,A成功还是失败,取决于数组中的每一个promise实例是成功还是失败,只要有一个是失败,A就是失败的,只有都成功A才是成功的
Promise.race
:最先知道状态的promise实例,是成功还是失败,决定了A是成功还是失败
function fn(interval) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(interval);
}, interval);
});
}
let p1 = fn(3000);
let p2 = fn(1000);
let p3 = Promise.resolve(0);
Promise.all([p1, p2, p3]).then(results => {
// 不论谁先知道状态,最后结果的顺序和传递数组的顺序要保持一致
console.log(results);
});
复制代码
3s后:
function fn(interval) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(interval);
}, interval);
});
}
let p1 = fn(3000);
let p2 = fn(1000);
let p3 = Promise.reject(0);
Promise.all([p1, p2, p3]).then(results => {
// 不论谁先知道状态,最后结果的顺序和传递数组的顺序要保持一致
console.log(results);
}).catch(reason => {
// 处理过程中,遇到一个失败,则All立即为失败,结果就是当前实例失败的原因
console.log(reason);
});
复制代码
直接立刻打印 0
需求处理:
const api1 = () => {
return new Promise(resolve => {
$.ajax({
url: '/api1',
success(result1) {
resolve(result1);
}
});
});
};
const api2 = () => {
return new Promise(resolve => {
$.ajax({
url: '/api2',
success(result2) {
resolve(result2);
}
});
});
};
const api3 = () => {
return new Promise(resolve => {
$.ajax({
url: '/api3',
success(result3) {
resolve(result3);
}
});
});
};
复制代码
api1 / api2 / api3串行请求:
api1().then(result1 => {
return api2();
}).then(result2 => {
return api3();
}).then(result3 => {
});
复制代码
更好的串行请求:
(async function () {
let result1 = await api1();
let result2 = await api2();
let result3 = await api3();
})();
复制代码
并行请求:
Promise.all([api1(), api2(), api3()]).then(results => {
});
复制代码
async
/ await
async
/ await
:ES7提出, generator
+ promise
的语法糖
awai
t表达式会暂停整个async
函数的执行进程并出让其控制权,只有当其等待的基于promise的异步操作被兑现或被拒绝之后才会恢复进程。promise的解决值会被当作该await
表达式的返回值。使用async
/await
关键字就可以在异步代码中使用普通的try
/catch
代码块
需要注意的点:
-
async:函数修饰符,控制函数返回promise实例
async function fn() { return 10; } console.log(fn()); 复制代码
-
函数内部执行报错,则返回失败的promise实例,值是失败的原因
async function fn() { console.log(a) } console.log(fn()); 复制代码
-
自己返回一个promise,以自己返回的为主
async function fn() { return Promise.resolve(1); } console.log(fn()); 复制代码
-
如果函数内部做了异常捕获,则还是成功态
async function fn() { try{ console.log(a) }catch(e){} return 10 } console.log(fn()); 复制代码
使用 async
的主要目的:是为了在函数内部使用 await
function fn() {
await 1; //Uncaught SyntaxError: await is only valid in async function
}
复制代码
await
后面应该放置一个promise实例。我们书写的代码不是,浏览器也会把其变为promise实例await 1
->await Promise.resolve(1)
。await
中断函数体中,await
表达式会暂停整个asyn
c函数的执行进程并出让其控制权。只有等待await
后面的promise实例是成功态之后,才会把之前暂停的代码继续执行,如果后面的promise实例是失败的,则下面的代码就不再执行了。await
是异步的微任务。函数体中遇到await
,下面的代码会暂停执行,会把他们当做一个任务,放置在EventQueue的微任务队列中
function api(interval) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(interval);
}, interval);
});
}
async function func() {
// await 1; //-> await Promise.resolve(1);
let result1 = await api(1000);
console.log(result1);
let result2 = await api(3000);
console.log(result2);
}
func();
复制代码
await的串并行
有下面两个函数:
var resolveAfter2Seconds = function resolveAfter2Seconds() {
console.log("starting slow promise");
return new Promise(resolve => {
setTimeout(function () {
resolve("slow");
console.log("slow promise is done");
}, 2000);
});
};
var resolveAfter1Second = function resolveAfter1Second() {
console.log("starting fast promise");
return new Promise(resolve => {
setTimeout(function () {
resolve("fast");
console.log("fast promise is done");
}, 1000);
});
};
复制代码
不同的运行方法有不同的结果:
相继(串行)
// sequential:相继的 /[sɪˈkwenʃl]/
var sequential = async function sequential() {
console.log('==SEQUENTIAL START==');
const slow = await resolveAfter2Seconds();
console.log(slow);
const fast = await resolveAfter1Second();
console.log(fast);
};
sequential()
复制代码
串行
同时(并行)
// concurrent:同时发生的 /[kənˈkʌrənt]/
var concurrent = async function concurrent() {
console.log('==CONCURRENT START with await==');
const slow = resolveAfter2Seconds();
const fast = resolveAfter1Second();
console.log(await slow);
console.log(await fast);
};
concurrent()
复制代码
前面两行会直接同步运行,什么时候遇到await,就会停止下来
awai
t中断函数体中,await
表达式会暂停整个asyn
c函数的执行进程并出让其控制权。只有等待await
后面的promise实例是成功态之后,才会把之前暂停的代码继续执行,如果后面的promise实例是失败的,则下面的代码就不再执行了。
await slow
相当于 await resolveAfter2Seconds()
所以结果为:
console.log(await slow);
console.log(await fast)
复制代码
什么时候等slow结束,什么时候打印两个
如果是这样:
// concurrent:同时发生的 /[kənˈkʌrənt]/
var concurrent = async function concurrent() {
console.log('==CONCURRENT START with await==');
const slow = resolveAfter2Seconds();
const fast = resolveAfter1Second();
console.log(await fast);
console.log(await slow);
};
concurrent()
复制代码
Promise.all
的同步方式:
var concurrentPromise = function concurrentPromise() {
console.log('==CONCURRENT START with Promise.all==');
return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()])
.then((messages) => {
console.log(messages[0]);
console.log(messages[1]);
});
};
复制代码
平行(也相当于并行)
// parallel:平行的 /[ˈpærəlel]/
var parallel = async function parallel() {
console.log('==PARALLEL with await Promise.all==');
await Promise.all([
(async () => {
let result = await resolveAfter2Seconds();
console.log(result);
})(),
(async () => {
let result = await resolveAfter1Second();
console.log(result);
})(),
]);
};
复制代码
两个字执行函数有自己内部的私有上下文,互不影响。在这两个自执行函数所在的上下文,都是同步运行两个函数的。所以相当于这样:
var parallel2 = async function parallel() {
console.log('==PARALLEL with await Promise.all==');
var slow = (async () => {
let result = await resolveAfter2Seconds();
console.log(result);
})()
var fast = (async () => {
let result = await resolveAfter1Second();
console.log(result);
})()
await Promise.all([slow,fast]);
};
复制代码
var parallelPromise = function parallelPromise() {
console.log('==PARALLEL with Promise.then==');
resolveAfter2Seconds().then((message) => console.log(message));
resolveAfter1Second().then((message) => console.log(message));
};
复制代码
输出一样: