es之Promise深刻理解

一、await和 async是干什么的

await操做符node

  • 等待一个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函数chrome

  • 定义异步函数,返回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
复制代码

二、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')
});
复制代码

结果以下:segmentfault

p1_1
p2_1
p1_2
p2_2
p1_3
p2_3
复制代码

Promise是基于微任务的(微任务文章点击连接),async/await可视为 Promise 的语法糖,一样基于微任务实现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() 会当即建立一个微任务,将传入的对应的回调推入微任务队列

三、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 为参数调用返回。bash

参数为thenable对象

Promise中resolve一个thenable对象,执行步骤如何? 在 Promise 中 resolve 一个 thenable 对象时,异步

  • 须要先将 thenable 转化为 Promsie,
  • 而后当即调用 thenable 的 then 方法

这个过程须要做为一个 job 加入微任务队列,以保证对 then 方法的解析发生在其余上下文代码的解析以后async

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 后输出。post

  • 一、同步任务中,promiseB中先(输出promiseB)promiseB中resolve被调用,同时向microtask插入任务PromiseResolveThenableJob。promiseA中(输出promiseA),同时向microtask插入任务1
  • 二、在 PromiseResolveThenableJob 执行中会执行 thenable.then(),从而注册了另外一个微任务out thenable。执行任务1(输出1),同时向microtask插入任务2。
  • 三、执行任务out thenable(输出out thenable 100),同时向microtask插入任务then2。执行任务2(输出2),同时向microtask插入任务3 。
  • 四、执行任务then2(输出then2),同时向microtask插入任务then3。执行任务3(输出3)。
  • 五、执行任务then3(输出then3),至此结束。

Promise.resolve(thenable)与new Promise(resolve=>(resolve(thenable)))的处理结果一致

正是因为规范中对 thenable 的处理须要在一个微任务中完成,从而致使了第一个 Promise 的后续回调被延后了1个时序

参数是一个 Promise 实例

  • 一、因为 Promise 实例是一个对象,其原型上有 then 方法,因此这也是一个 thenable 对象。
  • 二、一样的,浏览器会建立一个 PromiseResolveThenableJob 去处理这个 Promise 实例,这是一个微任务。
  • 三、在 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的状态。

  • 一、promiseA处于resolve状态,promiseB中resolve被调用,同时向microtask插入任务PromiseResolveThenableJob
  • 二、promiseA.then被调用, 向microtask插入任务1
  • 三、执行PromiseResolveThenableJob, 向microtask插入任务resolvePromise(如上面的promiseA.then(...))
  • 四、执行 任务1(即输出1),返回一个promise, 向microtask插入任务2
  • 五、执行resolvePromise, 此时promise终于变成resolve, 向microtask插入任务then1
  • 六、执行任务2(即输出2)
  • 七、执行任务then1(即输出then1),返回一个promise,向microtask插入任务then2
  • 八、执行任务then2(即输出then2),返回一个promise,向microtask插入任务then3,执行任务then3(即输出then3)

分析Promise.resolve(promiseA)状况

三、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 执行顺序

「译」更快的 async 函数和 promises

相关文章
相关标签/搜索