结论:
Promise 是用来解决函数回调嵌套的, async、await 是用来解决逻辑上的函数依赖的。
相关概念:
Promise是一个有状态的对象,用来规范回调函数,内容是一个function(function Promise() { [native code] }),内部接收一个function,参数为resolve,reject,用于处理耗时操作的等待。
let promise = new Promise(function(resolve,reject){
if (condition){
resolve(...);
} else {
reject(...);
}
});
处理成功后,调用resolve方法,处理失败调用reject方法。
三种状态:onfulfilled、onreject、onpending。
初始是onpending,一旦状态改变,则变成settled状态,不可再变。
promise.then(onfulfilled, onreject);
promise.then(onfulfilled).catch(onreject);(推荐)
promise对象then方法有两种处理异常方式,一种直接catch(两种状态分别绑定在两个promise对象上),一种传入onreject(两种状态都绑定在一个promise上,在 onFulfilled 中发生异常的话,在 onRejected 中是捕获不到这个异常的。)。
***每次调用then方法,都会返回一个新的promise对象。(所以可以无限制的链式调用)
简写方式:
let resolvePromise = Promose.resovle();
resolvePromise.then(funcA).then(funcB).catch(errorFuncC).then(funcD);
*** 如果funcA抛出异常或返回的是一个rejected状态的Promise对象,则会跳过funcB,直接进入catch。
async 标识符,用于标识函数是一个异步函数(async函数不会造成阻塞,如果是一个耗时操作则直接返回一个Promise{onpending}),async标识的函数一定返回的是一个Promise对象,如果不是,会自动new Promise,并把return的值传入,如果没值就是new Promise(undefined)。 在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句,这和普通返回 Promise 对象的函数一样。(await阻塞只能保证在async函数中的阻塞,出了该函数域,则无法保证)。
async 函数, 调用方式和普通函数调用一样。
await 标识符,用于标识是在等待一个 async 函数完成,只能用在async函数中(因为只有异步函数才能等待,await等待promise对象的状态变化,async能保证返回一个promise),如果是普通字符串,则直接返回该字符串,不会阻塞;如果是一个Promise对象,则会等待promise对象状态的改变才会继续,如果多个async函数同时执行,互相没有依赖关系,不能用两行:
await asyncFunc1();// 耗时2s
await asyncFunc2();// 耗时1s
最终耗时应该在3s+
正确方式:
Promise.all([asyncFunc1(), asyncFunc2()]);
如果两个函数有依赖,例如第二个函数依赖第一个函数的返回结果:
let promise = Promise.resolve();
promise.then(asyncFunc1()).then(asyncFunc2()); //第二个函数的入参,由第一个函数返回,有等待。
async/await 一般使用方式:
// simulate a cost time operation
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`this is a success operation`);
// reject(`this is a failed operation`);
console.log(`timeout over.`);
}, ms);
}).catch( e => {
return e;
});
}
// main method
async function pprint(value, ms) {
let res = await timeout(ms);
console.log(`${value} ${res}`);
// you can add a lot of functions here, just like timeout()
return 'biu biu biu';
}
// call
pprint('hello world, ', 2000).then((v) => {
console.log(`Hi ${v}`);
});
先创建了一个普通函数用于模拟耗时操作(timeout)并且直接返回了一个Promise对象,由于需要使用await来实现等待,因此我们创建了async标识的主函数(pprint),最后再以调用普通函数的方式调用主函数。
需要注意:
- async 函数本身不会等待,它会立刻返回,之所以await timeout();在主函数中能起到等待的作用,是因为timeout函数本身也没有需要等待的地方,它是直接返回了Promise,具体需要等待的是setTimeout里Promise对象的协同程序。如果不return new Promise,则不会得到resolve/reject的信息,如果默认没有返回值,直接setTimeout,使用了await的会返回一个undefined,普通调用的会返回一个Promise{undefined}
- 正是因为promise的不等待,才使得await函数必须写在async函数中
- await 后面跟的表达式,如果是一个函数调用,则会判断这个函数返回的内容是Promise对象还是普通的字符串,如果是Promise对象,则会阻塞住,等接到这个Promise后会取出它的回调值而不用Promise.then(value => console.log(value);)这样获取了,因为它等的就是值。
- 如果调用async函数不使用await,则async会立刻返回一个Promise {pending},如果是直接返回了值而不需要等待的话会返回Promise {val},这时要取val,就要使用 .then(val => {...})
- 使用async/await的初衷,是为了控制主流程的执行顺序,如果函数的执行时序和执行结果不会对主流程造成影响,则不考虑使用,因为这样做会造成不必要的时间消耗