1、await和 async是干什么的
await操作符
- 等待一个Promise对象
- 只能再async函数中使用
- 如果操作符后的表达式不是一个Promise,则返该值本身
async function fun(){
var val = await 20;
console.log(val);
}
fun();
//20
async function bar(){
var val = await Promise.resolve(20);
console.log(val);
}
bar();
//20
async函数
- 定义异步函数,返回Promise实例,
- 当 async 函数返回一个值时,Promise 的 resolve 方法负责传递这个值
- 当 async 函数抛出异常时,Promise 的 reject 方法会传递这个异常值
async function bar(name){
var subName = await Promise.resolve('fen');
return name + subName;
}
bar('li').then((res)=>{
console.log(res);
});
//lifen
2、Promise的链式then()是怎样执行的
new Promise((resolve) => {
resolve();
}).then(() => {
console.log('p1_1')
}).then(() => {
console.log('p1_2')
}).then(() => {
console.log('p1_3')
});
new Promise((resolve) => {
resolve();
}).then(() => {
console.log('p2_1')
}).then(() => {
console.log('p2_2')
}).then(() => {
console.log('p2_3')
});
结果如下:
p1_1
p2_1
p1_2
p2_2
p1_3
p2_3
Promise是基于微任务的(微任务文章点击链接),async/await可视为 Promise 的语法糖,同样基于微任务实现
分析
- Promise 多个 then() 链式调用,并不是连续的创建了多个微任务并推入微任务队列,因为 then() 的返回值必然是一个 Promise,而后续的 then() 是上一步 then() 返回的 Promise 的回调
- 传入 Promise 构造器的执行器函数内部的同步代码执行到 resolve(),将 Promise 的状态改变为Promise {: undefined},然后 then 中传入的回调函数 console.log('p1_1') 作为一个微任务被推入微任务队列
- 第二个 then() 中传入的回调函数 console.log('p1_2') 此时还没有被推入微任务队列,只有上一个 then() 中的 console.log('p1_1') 执行完毕后,console.log('p1_2') 才会被推入微任务队列
总结
- Promise的then()链式调用,是通过then() 返回一个新的Promise实现的
- 如果 Promise 的状态是 pending,那么 then 会在该 Promise 上注册一个回调,当其状态发生变化时,对应的回调将作为一个微任务被推入微任务队列
- 如果 Promise 的状态已经是 fulfilled 或 rejected,那么 then() 会立即创建一个微任务,将传入的对应的回调推入微任务队列
3、Promise.resolve()与new Promise(resolve=>(resolve))的区别
EnquequeJob存放两种类型的任务, 即PromiseResolveThenableJob和PromiseReactionJob, 并且都是属于microtask类型的任务。
PromiseReactionJob: 可以通俗的理解为promise中的回调函数 PromiseResolveThenableJob(promiseToResolve, thenable, then): 创建和 promiseToResolve 关联的 resolve function 和 reject function。以 then 为调用函数,thenable 为this,resolve function和reject function 为参数调用返回。
参数为thenable对象
Promise中resolve一个thenable对象,执行步骤如何? 在 Promise 中 resolve 一个 thenable 对象时,
- 需要先将 thenable 转化为 Promsie,
- 然后立即调用 thenable 的 then 方法
这个过程需要作为一个 job 加入微任务队列,以保证对 then 方法的解析发生在其他上下文代码的解析之后
let thenable = {
then(resolve, reject) {
console.log('in thenable');
resolve(100);
}
};
var promiseB = new Promise((resolve) => {
console.log('promiseB');
resolve(thenable);
});
//或var promiseB = Promise.resolve(thenable);
var promiseA = new Promise((resolve) => {
console.log('promiseA');
resolve();
});
promiseB.then((res) => {
console.log('out thenable ' + res)
}).then(() => {
console.log('then2')
}).then(() => {
console.log('then3')
});
promiseA.then(() => {
console.log(1);
}).then(() => {
console.log(2)
}).then(() => {
console.log(3)
});
结果如下:
promiseB
promiseA
in thenable
1
out thenable 100
2
then2
3
then3
分析
in thenable 后于 promiseA 而先于 1 输出,同时out thenable 100 在 1 后输出。
- 1、同步任务中,promiseB中先(输出promiseB)promiseB中resolve被调用,同时向microtask插入任务PromiseResolveThenableJob。promiseA中(输出promiseA),同时向microtask插入任务1
- 2、在 PromiseResolveThenableJob 执行中会执行 thenable.then(),从而注册了另一个微任务out thenable。执行任务1(输出1),同时向microtask插入任务2。
- 3、执行任务out thenable(输出out thenable 100),同时向microtask插入任务then2。执行任务2(输出2),同时向microtask插入任务3 。
- 4、执行任务then2(输出then2),同时向microtask插入任务then3。执行任务3(输出3)。
- 5、执行任务then3(输出then3),至此结束。
Promise.resolve(thenable)与new Promise(resolve=>(resolve(thenable)))的处理结果一致
正是由于规范中对 thenable 的处理需要在一个微任务中完成,从而导致了第一个 Promise 的后续回调被延后了1个时序
参数是一个 Promise 实例
- 1、由于 Promise 实例是一个对象,其原型上有 then 方法,所以这也是一个 thenable 对象。
- 2、同样的,浏览器会创建一个 PromiseResolveThenableJob 去处理这个 Promise 实例,这是一个微任务。
- 3、在 PromiseResolveThenableJob 执行中,执行了 Promise.prototype.then,而这时 Promise 如果已经是 resolved 状态 ,then 的执行会再一次创建了一个微任务
var promiseA = Promise.resolve('promiseA');
var promiseB = new Promise((resolve) => {
resolve(promiseA);
});
//或var promiseB = Promise.resolve(promiseA);
promiseB.then(() => {
console.log('then1')
}).then(() => {
console.log('then2')
}).then(() => {
console.log('then3')
});
promiseA.then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
});
结果
//new Promise
1
2
then1
3
then2
then3
//采用或的结构Promise.resolve(promiseA)
then1
1
then2
2
then3
3
分析new Promise(resolve=>(resolve(promiseA)))情况
'then1'输出晚了两个时序。对于参数是一个promise ,resolve(promiseA)究竟如何工作的?
例子中,当promiseB被resolved的时候, 也就是将一个promise(代指A)当成另外一个promise(代指B)的resolve参数,会向EnquequeJob插入一个PromiseResolveThenableJob任务。PromiseResolveThenableJob大概做了如下的事情:
() => {
promiseA.then(
resolvePromiseB,
rejectPromiseB
);
}
并且当resolvePromiseB执行后, promiseB的状态才变成resolve,也就是B追随A的状态。
- 1、promiseA处于resolve状态,promiseB中resolve被调用,同时向microtask插入任务PromiseResolveThenableJob
- 2、promiseA.then被调用, 向microtask插入任务1
- 3、执行PromiseResolveThenableJob, 向microtask插入任务resolvePromise(如上面的promiseA.then(...))
- 4、执行 任务1(即输出1),返回一个promise, 向microtask插入任务2
- 5、执行resolvePromise, 此时promise终于变成resolve, 向microtask插入任务then1
- 6、执行任务2(即输出2)
- 7、执行任务then1(即输出then1),返回一个promise,向microtask插入任务then2
- 8、执行任务then2(即输出then2),返回一个promise,向microtask插入任务then3,执行任务then3(即输出then3)
分析Promise.resolve(promiseA)情况
3、await/async函数和Promise怎么转换
async function async1() {
console.log('async1')
await async2();
console.log('async1 end')
}
async function async2() {
console.log('async2');
}
async1();
new Promise(function (resolve) {
console.log('promise')
resolve();
}).then(function () {
console.log(1);
}).then(function () {
console.log(2);
}).then(function () {
console.log(3);
})
浏览器chrome76 环境中,结果如下:
//浏览器chrome76
async1
async2
promise
async1 end
1
2
3
node环境中,async1 end被推迟了2个时序
//node v10.6.0,
async1
async2
promise
1
2
async1 end
3
以浏览器运行结果来转换
function async1(){
console.log('async1 start');
const p = async2();
return Promise.resolve(p).then(() => {
console.log('async1 end')
});
}
function async2(){
console.log('async2');
return Promise.resolve();
}
//其他代码同上
总结
- Promise.resolve(v) 不等于 new Promise(resolve => resolve(v)),因为如果 v 是一个 Promise 对象,前者会直接返回 v,而后者需要经过一系列的处理(主要是 PromiseResolveThenableJob)
- 宏任务的优先级是高于微任务的
参考链接: 令人费解的 async/await 执行顺序