Promise.resolve(x)与new Promise(r=>r(x))

652 阅读12分钟

Promise.resolve(x)与new Promise(r=>r(x))

本文主要关于 Promise.resolve(x) 与 new Promise(r=>r(x)) 传入几种不同类型的参数 x, 二者处理的差异性。 产生差异性的原因是由于PromiseResolveThenableJob的处理机制,同时拓展至async函数的await操作实际原理。

1,Promise.resolve(简单值) 与 new Promise(r=>r(简单值))(非Promise实例与非Thenable对象为简单值)

new Promise(r => r())                           // 1
    .then(_ => console.log('then1'))            // 2
    .then(_ => console.log('then2'))            // 3
    .then(_ => console.log('then3'))            // 4

Promise.resolve(1)                              // 5
    .then(_ => console.log('resolve_then1'))    // 6
    .then(_ => console.log('resolve_then2'))    // 7
    .then(_ => console.log('resolve_then3'))    // 8

// 打印结果:
// then1
// resolve_then1
// then2
// resolve_then2
// then3
// resolve_then3

执行顺序过程:

  • 1 , 执行代码,首先执行到第2行出现then回调,于是微任务队列放入then1微任务(此处命名根据其打印数据来,此处打印数据为"then1",所以叫then1微任务),继续执行同步代码。
    此时微任务队列[then1微任务]
    打印结果:

  • 2 ,同步代码执行到第5行,此处Promise.resolve(1)返回一个值为1的Promise对象,继续执行第6行发现resolve_then1微任务,放入微任务队列,至此同步代码执行完毕。
    此时微任务队列[then1微任务,resolve_then1微任务]
    打印结果:

  • 3,取出微任务队列第一个then1微任务执行,打印then1,then1微任务执行完毕到第3行发现then2微任务,放入微任务队列。
    此时微任务队列[resolve_then1微任务,then2微任务]
    打印结果:then1

  • 4,继续取出微任务队列第一个resolve_then1微任务执行,打印resolve_then1,代码执行到第7行发现resolve_then2微任务,放入微任务队列。
    此时微任务队列[then2微任务,resolve_then2微任务] 打印结果:then1,resolve_then1

  • 5,继续取出微任务队列第一个then2微任务执行,打印then2,then2微任务执行完毕到第4行发现then3微任务,放入微任务队列。
    此时微任务队列[resolve_then2微任务,then3微任务] 打印结果:then1,resolve_then1,then2

  • 6,继续取出微任务队列第一个resolve_then2微任务执行,打印resolve_then2,代码执行到第8行发现resolve_then3微任务,放入微任务队列。
    此时微任务队列[then3微任务,resolve_then3微任务]
    打印结果:then1,resolve_then1,then2,resolve_then2

  • 7,继续取出微任务队列第一个then3微任务执行,打印then3,then3微任务执行完毕,无任何微任务加入,取出resolve_then3微任务执行,打印resolve_then3,resolve_then3微任务执行完毕,无任何微任务加入,事件循环结束。
    此时微任务队列[]
    打印结果:then1,resolve_then1,then2,resolve_then2,then3,resolve_then3

总结:此处的Promise.resolve(简单值)与new Promise(r=>r(简单值))执行顺序相同。

2,Promise.resolve(Promise实例)与new Promise(r=>r(Promise实例))

new Promise(r => r(new Promise(r => r())))      // 1
   .then(_ => console.log('then1'))             // 2
   .then(_ => console.log('then2'))             // 3
   .then(_ => console.log('then3'))             // 4

Promise.resolve(new Promise(r => r()))		// 5
   .then(_ => console.log('resolve_then1'))	// 6
   .then(_ => console.log('resolve_then2'))	// 7
   .then(_ => console.log('resolve_then3'))	// 8
   
// 打印结果:
// resolve_then1
// resolve_then2
// then1
// resolve_then3
// then2
// then3

当传入参数为一个Promise实例的时候,打印顺序与传入简单值的时候出现了差异。我们发现与传入简单值相比,二者区别只在于传入的是个Promise实例,那么问题应该就出在传入Promise实例中。

  • Promise.resolve(简单值): 传入简单值的时候会生成一个Promise对象。并且状态为成功,值为传入的该简单值。

  • new Promise(r => r(简单值)): 传入简单值的时候会生成一个Promise对象。并且状态为成功,值为传入的该简单值。
    这个时候 二者对简单值的处理方式没有差异

  • Promise.resolve(Promise实例): 传入Promise实例的时候会原封不动的返回该Promise实例。

  • new Promise(r => r(Promise实例)) : 传入Promise实例的时候,new Promise(r => r(Promise实例)) 将会分成两步异步微任务操作,我们称呼为PromiseResolveThenableJob。见如下代码。

 // 原代码
new Promise(r => r(Promise实例)).then(做其他事)
 // 转换为进行PromiseResolveThenableJob处理代码
new Promise((resolve,reject)=>{
        // Promise实例.then(PromiseResolveThenableJob①): 第一步获取Promise实例最终状态与值,这一步为异步操作(因为使用了Promise的.then,所以进行了异步动作,这一步标注为)
        // (resolve_,reject_)(PromiseResolveThenableJob②) : 第二步使用resolve_函数与reject_函数同步Promise实例最终状态与值给外层的Promise,这个过程也是个异步操作
	Promise实例.then(resolve_,reject_) 
}).then(做其他事)

通过上面的的代码转换,我们可以看到,相较于Promise.resolve(Promise实例),new Promise(r => r(Promise实例)) 实则多进行了两步异步操作,就是代码中标注的PromiseResolveThenableJob①与PromiseResolveThenableJob②,有了这部分的代码转换,我们接下来继续分析二者的事件循环。

执行顺序过程:

  • 1 , 执行代码第一行,发现是PromiseResolveThenableJob,于是先将PromiseResolveThenableJob①放入微任务队列。
    此时微任务队列[PromiseResolveThenableJob①]
    打印结果:
  • 2 , 继续执行同步代码第5行,第5行无任何异步操作,单纯的返回当前传入的Promise实例,继续执行至第6行,发现resolve_then1微任务,放入微任务队列。
    此时微任务队列[PromiseResolveThenableJob①,resolve_then1微任务]
    打印结果:
  • 3 , 当前无任何同步代码需要执行,取出微任务队列第一个微任务PromiseResolveThenableJob①执行,执行完毕发现PromiseResolveThenableJob②,放入任务队列。
    此时微任务队列[resolve_then1微任务,PromiseResolveThenableJob②]
    打印结果:
  • 4 ,取出微任务队列第一个微任务resolve_then1执行,打印resolve_then1,继续执行到代码第7行,发现resolve_then2微任务,放入任务队列。
    此时微任务队列[PromiseResolveThenableJob②,resolve_then2微任务]
    打印结果:resolve_then1
  • 5 ,取出微任务队列第一个微任务PromiseResolveThenableJob②执行,将new Promise(r => r(Promise实例))中Promise实例的状态与值同步给外层new Promise,执行完毕,代码执行到第2行,发现then1微任务,放入任务队列。
    此时微任务队列[resolve_then2微任务,then1微任务]
    打印结果:resolve_then1
  • 6 ,取出微任务队列第一个微任务,resolve_then2微任务执行,打印resolve_then2,代码继续执行到第8行,发现resolve_then3微任务,放入微任务队列。
    此时微任务队列[then1微任务,resolve_then3微任务]
    打印结果:resolve_then1,resolve_then2
  • 7 ,取出微任务队列第一个微任务,then1微任务执行,打印then1,代码继续执行到第3行,发现then2微任务,放入微任务队列。
    此时微任务队列[resolve_then3微任务,then2微任务]
    打印结果:resolve_then1,resolve_then2,then1
  • 8 ,取出微任务队列第一个微任务,resolve_then3微任务执行,打印resolve_then3,第8行执行完毕,无任何代码执行。 此时微任务队列[then2微任务]
    打印结果:resolve_then1,resolve_then2,then1,resolve_then3
  • 9 ,取出微任务队列第一个微任务,then2微任务执行,打印then2,第3行执行完毕,执行到第4行,发现then3微任务,放入微任务队列。 此时微任务队列[then3微任务]
    打印结果:resolve_then1,resolve_then2,then1,resolve_then3,then2
  • 10 ,取出微任务队列第一个微任务,then3微任务执行,打印then3,第4行执行完毕,无任何代码需要执行,整体代码执行完毕。 此时微任务队列[]
    打印结果:resolve_then1,resolve_then2,then1,resolve_then3,then2,then3

总结:此处的Promise.resolve(Promise实例)与new Promise(r=>r(Promise实例))执行顺序不同。

3,Promise.resolve(thenable对象) 与 new Promise(r=>r(thenable对象))

// **thenable对象:** 简而言之,具有then方法的函数或对象,如下thenable_Object。
const thenable_Object = { then(resolve, reject) { console.log('2'); resolve()} }  // 0

new Promise(r => { console.log('then0'); r(thenable_Object) })    // 1
    .then(_ => console.log('then1'))    			  // 2
    .then(_ => console.log('then2'))    			  // 3
    .then(_ => console.log('then3'))    			  // 4

Promise.resolve(thenable_Object)				  // 5
    .then(_ => console.log('resolve_then1'))			  // 6
    .then(_ => console.log('resolve_then2'))			  // 7
    .then(_ => console.log('resolve_then3')) 			  // 8

console.log('script_start');					  // 9

// 打印结果
// then0
// script_start
// thenable
// thenable
// then1
// resolve_then1
// then2
// resolve_then2
// then3
// resolve_then3

关于thenable对象,我个人理解:

  • 当传入参数为Promise实例的时候,Promise.resolve(Promise实例)过程为同步过程返回该Promise实例,无任何异步操作,new Promise(r=>r(Promise实例))会经过两个异步操作去同步状态然后处理下面的其他事情。

  • 而当传入thenable对象的时候,根据代码我们发现Promise.resolve(thenable对象)与new Promise(r=>r(thenable对象))都进行了一个异步操作,我直接说结论:

    Promise.resolve(thenable对象)与new Promise(r=>r(thenable对象))都会将当前的thenable对象转换为Promise对象,该过程是同步过程,然后执行其自定义的then方法,该过程为异步操作,then方法传入的两个参数为最终要返回的Promise实例的resolve与reject函数,通过这两个函数改变外层的Promise实例的状态,该过程只涉及一个异步操作(执行then回调)。下面是二者对thenable的处理伪代码

// new Promise(r => { console.log('then0'); r(thenable_Object) }),
// Promise.resolve(thenable_Object)  
// 最终会进行如下转换

new Promise((resolve, reject) => {
    // 将thenable_Object转化成Promise实例(thenable_Object_Promise),该过程为同步过程
    let thenable_Object_Promise = thenable转化Promise函数(thenable_Object)
    // 执行thenable_Object_Promise的then方法 完成对外层Promise状态的最终改变
    thenable_Object_Promise.then(resolve, reject)
})

执行顺序过程:

  • 1 同步代码执行至第2行,打印then0,发现resolve的是thenable对象,将thenable对象转化为Promise实例,执行thenable对象对象的then函数(thenable微任务①),此过程为异步,放入任务队列。
    此时微任务队列:[thenable微任务①]
    打印结果:then0,

  • 2 同步代码执行至第5行,发现Promise.resolve的是thenable对象,将thenable对象转化为Promise实例,执行thenable对象对象的then函数(thenable微任务②),此过程为异步,放入任务队列。
    此时微任务队列:[thenable微任务①,thenable微任务②]
    打印结果:then0,

  • 3 同步代码执行至第9行,打印script_start,同步代码执行完毕。
    此时微任务队列:[thenable微任务①,thenable微任务②]
    打印结果:then0,script_start

  • 4 取出任务队列第一个thenable微任务①,执行并打印thenable,完成对外层Promise(new Promise(r => { console.log('then0'); r(thenable_Object) }))状态的最终改变,继续执行代码至第2行发现then1微任务,加入任务队列。
    此时微任务队列:[thenable微任务②,then1微任务]
    打印结果:then0,script_start,thenable

  • 5 取出任务队列第一个thenable微任务②,执行并打印thenable,,完成对外层Promise(Promise.resolve(thenable_Object))状态的最终改变,继续执行代码至第6行发现resolve_then1微任务,加入任务队列。
    此时微任务队列:[then1微任务,resolve_then1微任务]
    打印结果:then0,script_start,thenable,thenable

  • 6 取出任务队列第一个then1微任务,执行并打印then1,代码执行至第3行,发现then2微任务,加入任务队列。
    此时微任务队列:[resolve_then1微任务,then2微任务]
    打印结果:then0,script_start,thenable,thenable,then1

  • 7 取出任务队列第一个resolve_then1微任务,执行并打印resolve_then1,代码执行至第7行,发现resolve_then2微任务,加入任务队列。
    此时微任务队列:[then2微任务,resolve_then2微任务]
    打印结果:then0,script_start,thenable,thenable,then1,resolve_then1

  • 8 取出任务队列第一个then2微任务,执行并打印then2,代码执行至第4行,发现then3微任务,加入任务队列。
    此时微任务队列:[resolve_then2微任务,then3微任务]
    打印结果:then0,script_start,thenable,thenable,then1,resolve_then1,then2

  • 9 取出任务队列第一个resolve_then2微任务,执行并打印resolve_then2,代码执行至第8行,发现resolve_then3微任务,加入任务队列。
    此时微任务队列:[then3微任务,resolve_then3微任务]
    打印结果:then0,script_start,thenable,thenable,then1,resolve_then1,then2,resolve_then2

  • 10 取出任务队列第一个then3微任务,执行并打印then3
    此时微任务队列:[resolve_then3微任务]
    打印结果:then0,script_start,thenable,thenable,then1,resolve_then1,then2,resolve_then2,then3

  • 11 取出任务队列最后一个resolve_then3微任务,执行并打印resolve_then3,代码执行完。
    此时微任务队列:[]
    打印结果:then0,script_start,thenable,thenable,then1,resolve_then1,then2,resolve_then2,then3,resolve_then3

总结:此处的Promise.resolve(thenable对象)与new Promise(r=>r(thenable对象))执行顺序相同。

async函数await原理以及其在node -v10与node -v13差异

await到底做了什么

// 当前node环境 version-13
async function fn1() {      		
   console.log('start');   		
   await fn2()   
   // do sth
   console.log('end');     	
}
function fn2() {            	
   Promise.resolve()       		
       .then(_ => console.log('1'))    
       .then(_ => console.log('2'))    
       .then(_ => console.log('3'))     
   return undefined                    
}                                     

fn1()

// 打印结果
// start
// 1
// end
// 2
// 3

为了更好的理解打印顺序,下面直接上对await进行转换之后的代码

 // 当前node环境 version-13
 async function fn1() {      		
    console.log('start');   		
    // await fn2()  
    // do sth
    // console.log('end');  
    Promise.resolve(fn2())
    	.then(res=>{
		// do sth
   		console.log('end');  
	})
}
function fn2() {            	
    Promise.resolve()       		
        .then(_ => console.log('1'))    
        .then(_ => console.log('2'))    
        .then(_ => console.log('3'))     
    return undefined                    
}                                     

fn1()

// 打印结果
// start
// 1
// end
// 2
// 3

显而易见,await做的事就是将其后面跟随的东西(fn2())的结果进行Promise.resolve返回一个Promise,同时await下一行往后的代码(// do sth ;console.log('end'); )全放在刚才resolve出来的Promise的then回调中执行,以此达到等待(异步操作返回结果)的功能。

理解事件循环的朋友应该可以根据对await进行转换的代码分析出执行顺序,此处不再赘述,如需要代码分析请留言。

node10 与 node13 await表现形式的差异

node 13中的await

// 当前node环境 version-13
async function fn1() {
    console.log('start');
    await fn2()
    // do sth
    console.log('end');
}
// 注意 此处的fn2是个async函数 
async function fn2() {
    Promise.resolve()
        .then(_ => console.log('1'))
        .then(_ => console.log('2'))
        .then(_ => console.log('3'))
    return undefined
}

fn1()

 // 打印结果
//  start
//  1
//  end
//  2
//  3

下面对await进行转换后的代码

// 当前node环境 version-13
async function fn1() {
    console.log('start');
    Promise.resolve(fn2()) .then(_=>{
		// do sth
    		console.log('end');
	})
}
// 注意 此处的fn2是个async函数 
async function fn2() {
    Promise.resolve()
        .then(_ => console.log('1'))
        .then(_ => console.log('2'))
        .then(_ => console.log('3'))
    return undefined
}

fn1()

 // 打印结果
//  start
//  1
//  end
//  2
//  3

此处代码与前面代码唯一差异就是fn2现在是async函数,所以在 Promise.resolve(fn2())的时候,fn2()执行完毕返回的结果是个Promise实例(该实例最终结果为fn2中return的结果,如果没有写return,则相当于return undefined)。依赖于转换await之后的代码很容易知道其执行顺序

node 10中的await

// 当前node环境 version-10
async function fn1() {
    console.log('start');
    await fn2()
    // do sth
    console.log('end');
}
// 注意 此处的fn2是个async函数 
async function fn2() {
    Promise.resolve()
        .then(_ => console.log('1'))
        .then(_ => console.log('2'))
        .then(_ => console.log('3'))
    return undefined
}

fn1()

 // 打印结果
//  start
//  1
//  2
//  3
//  end

我们发现node10中输出结果于node13中不一样,我们继续对node10中的await进行转换

// 当前node环境 version-10
async function fn1() {
    console.log('start');
    new Promise(r=>r(fn2()))
    	.then(_=>{
         	// do sth
          	console.log('end');
	})    
}
// 注意 此处的fn2是个async函数 
async function fn2() {
    Promise.resolve()
        .then(_ => console.log('1'))
        .then(_ => console.log('2'))
        .then(_ => console.log('3'))
    return undefined
}

fn1()

 // 打印结果
//  start
//  1
//  2
//  3
//  end

我们发现,node10对await的处理与node13不一样,node10采用的是new Promise(r=>r(fn2()))的方式,而node13是Promise.resolve(fn2()) 的方式,继续分析,由于fn2是个async函数,最终返回结果是个Promise实例,所以node10相当于new Promise(r=>r(Promise实例)),node13则为Promise.resolve(Promise实例),回顾之前说的二者的差异性,所以node10在执行上会比node13多两个时序,这就是node10与node13上await表现的差异,但是我们按照最新的版本来,所以正常情况我们按照node13的版本结果为准。

3951