资讯专栏INFORMATION COLUMN

理解promise--一个问题引发的思考

widuu / 869人阅读

这篇文章动机是为了解释这个问题

往下阅读之前你需要知道,promise的resolve回调函数会被放在job queue中等待主任务执行完毕后等待执行(这也是文章提到的问题的解答)。可以参考这篇文章

代码一
new Promise((resolve, reject) => {
    resolve();  //将resolved回调函数(then的第一个参数)添加到queue队列
}).then(() => {
    console.log("promise1 resolved");
});

new Promise((resolve, reject) => {
    resolve();  //将resolved回调函数(then的第一个参数)添加到queue队列
}).then(() => {
    console.log("promise3 resoved");
});
console.log("main");

//result:
//main
//promise1 resolved
//promise3 resoved

这个结果很好理解,两个resolve()函数将两个回调函数依次添加到job queue队列,主任务队列执行完后,依次执行job queue中的任务。

代码二

在看代码前首先要理解promise then函数的返回值也是一个promise,而返回的promise的状态(pending,resolved,reject)在不同情况下会是不同的值,具体请参考MDN上的解释。为方便理解,请大家记住下面这段代码中的then函数的返回值均是处于resolved状态的promise。并请牢记一个promise如果是resolved状态则它会将其then回调函数作为一个任务添加到job queue。为方便解释,我会在代码中将每个then函数标记为一个任务,希望大家能对照着看。OK,让我们来看代码

new Promise((resolve, reject) => {
    resolve();  //resolve_1
}).then(() => {  // then_task_1
    console.log("promise1 resolved");
}).then(() => {  // then_task_2
    console.log("promise2 resolved");
}).then(() => {  // then_task_3
    console.log("promise3 resolved");
});

new Promise((resolve, reject) => {
    resolve();  //resolve_2
}).then(() => {  // then_task_x
    console.log("promisex resolved");
}).then(() => {  // then_task_y
    console.log("promisey resolved");
}).then(() => {  // then_task_z
    console.log("promisez resolved");
});

console.log("main");

//result:
//main
//promise1 resolved
//promisex resolved
//promise2 resolved
//promisey resolved
//promise3 resolved
//promisez resolved

1,首先resolve_1将then_task_1添加到job queue,然后resolve2将then_task_x添加到job queue。然后执行到console.log("main")。
主任务队列中的任务执行完成,主任务队列空(是的,这时job queue中只有then_task_1和then_task_x)。
2,开始执行job queue中的任务:执行then_task_1,打印promise1,这时返一个resolved promise,这个promise的then是then_task_2,js将then_task_2添加到then_task_x后;执行then_task_x,打印promisx,同理将then_task_y添加到then_task_2后。依次类推,我们就看到了代码结果这样的打印顺序。

代码三
new Promise((resolve, reject) => {
    resolve(Promise.resolve().then(() => { //then_task_innner
        console.log("inner promise resolved") 
    })); //外层resolve对应then_task1,内层resolve对应then_task_inner
}).then(() => { //then_task_1
    console.log("promise1 resolved");
});
new Promise((resolve, reject) => {
    resolve();  //resolve2
}).then(() => { //then_task_2
    console.log("promise2 resolved");
});
console.log("main");

//result:
//main
//inner promise resolved
//promise2 resolved
//promise1 resolved

这段代码执行到第一个resolve时发现其参数是一个resolved promise的then回调函数,这个参数是无法立即计算出值来的(因为这个then_task_innner被添加到job queue不会被立即执行)。所以这个resolve函数不会被立即执行。所以到这里then_task_innner被添加到job queue了,但是then_task_1并没有,因为其对应的promise还处于pending状态,没有被resolve。然后执行到resolve2,将then_task_2添加到job queue。然后执行console.log("main"),主任务队列完成。这时job queue中有then_task_inner和then_task_2。当执行完then_task_inner后,第一个resolve()会被添加到job queue,这时job queue中只有resolve()这个任务,这个resolve被执行。其对应的promise变为resolved状态,对应的then_task_1被添加到job queue中,然后被执行。因此我们看到屏幕上的打印结果是这样的。

代码四

有了代码三的铺垫,我们现在来看看下面这段代码。

new Promise((resolve, reject) => {
    resolve(Promise.resolve());  //为方便解释,我们将外层的resolve叫做 outer_resolve;内层的resolve叫做inner_resolve
}).then(() => {  //promise1_task
    console.log("promise1 resolved");
});
new Promise((resolve, reject) => {
    resolve();  //resolve2 对应 promise2_task
}).then(() => {  //promise2_task
    console.log("promise2 resolved");
}).then(() => {  //promise3_task
    console.log("promise3 resolved");
});
console.log("main");

//result:
//main
//promise2 resolved
//promise3 resolved
//promise1 resolved

放这段代码是为了过渡,方便解释下面的问题。
这段代码对比代码三inner_resolve没有了.then();不过并不是没有了,我们可以理解为inner_resolve对应的promise的then函数是null。
程序首先运行到第一个resolve,发现无法立即获得参数值,对应的promise无法改变状态,任然是pendding,所以对应的promise1_task不会被添加到job queue。然而inner_resolve被执行了,其对应的then函数(null)被添加到job queue。接下来第二个resolve被执行,对应的promise2_task被添加到job queue。之后主任务执行完成,开始执行job queue中的任务。对一个任务是null,执行完后因为第一个resolve的参数有了,所以这个resolve函数被添加到job queue中;接下来是执行promise2_task,打印promise2 并将promise3_task添加到job queue。然后执行第一个resolve函数,其对应的promise1_task被添加到job queue。接下来是执行promise3_task,然后promise1_task。
如果我们将resolve(Promise.resolve())换成resolve()。就会看到打印结果顺序是promise1,promise2,promise3。

回到最初的问题
new Promise((resolve, reject) => {
    console.log("async1 start");
    console.log("async2");
    resolve(Promise.resolve());  //这里的Promise.resolve会添加一个null任务到job queue,外层resolve对应async1_end_task
}).then(() => {   //async1_end_task
    console.log("async1 end");
});

new Promise(function(resolve) {
    console.log("promise1");
    resolve();  //对应promise2_task
}).then(function() {  //promise2_task
    console.log("promise2");
}).then(function() { //promise3_task
    console.log("promise3");
}).then(function() { //promise4_task
    console.log("promise4");
});

//result:
//async1 start
//async2
//promise1
//promise2
//promise3
//async1 end
//promise4

这里我们对async1 start,async2,promise1的打印顺序不解释,这时在主任务中执行的,按主任务执行顺序打印。
romise2,promise3,async1 end,promise4的打印顺序解释同代码四

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99058.html

相关文章

  • 一道setTimeout async promise执行顺序笔试题引发思考

    摘要:如果你要问他和谁当进去的快,要从下面两个方面考虑结束时。至于什么,查了很多的资料,了解到一个浏览器环境只能有一个事件循环,而一个事件循环可以有多个任务队列。 ====据说这是今日头条去年的一道笔试题,主要考察的是setTimeout async promise执行顺序 ~先双手奉上这道题目~ async function async1() { consol...

    soasme 评论0 收藏0
  • 一道面试题引发思考 --- Event Loop

    摘要:想必面试题刷的多的同学对下面这道题目不陌生,能够立即回答出输出个,可是你真的懂为什么吗为什么是输出为什么是输出个这两个问题在我脑边萦绕。同步任务都好理解,一个执行完执行下一个。本文只是我对这道面试题的一点思考,有误的地方望批评指正。 想必面试题刷的多的同学对下面这道题目不陌生,能够立即回答出输出10个10,可是你真的懂为什么吗?为什么是输出10?为什么是输出10个10?这两个问题在我脑...

    betacat 评论0 收藏0
  • 谈一谈几种处理JavaScript异步操作办法

    摘要:问题是处理完了,却也引发了自己的一些思考处理的异步操作,都有一些什么方法呢一回调函数传说中的就是来自回调函数。而回调函数也是最基础最常用的处理异步操作的办法。 引言 js的异步操作,已经是一个老生常谈的话题,关于这个话题的文章随便google一下都可以看到一大堆。那么为什么我还要写这篇东西呢?在最近的工作中,为了编写一套相对比较复杂的插件,需要处理各种各样的异步操作。但是为了体积和兼容...

    曹金海 评论0 收藏0
  • try catch引发性能优化深度思考

    摘要:进一步最终定位发现如果为的时候,效率骤降,如果为合法的字符串的时候,效率是正常值。每次执行该子句都会发生这种情况,将捕获的异常对象分配给一个变量。尽可能将它们与其他代码隔离,以免影响其性能。关键代码拆解成如下图所示(无关部分已省略):起初我认为可能是这个 getRowDataItemNumberFormat 函数里面某些方法执行太慢,从 formatData.replace 到 unesca...

    番茄西红柿 评论0 收藏2637
  • 好文 - 收藏集 - 掘金

    摘要:好吧,本文的主题可能还深入剖析的深复制前端掘金一年前我曾写过一篇中的一种深复制实现,当时写这篇文章的时候还比较稚嫩,有很多地方没有考虑仔细。 翻译 | 深入理解 CSS 时序函数 - 前端 - 掘金作者:Nicolas(沪江前端开发工程师) 本文原创,转载请注明作者及出处。 各位,赶紧绑住自己并紧紧抓牢了,因为当你掌握了特别有趣但又复杂的CSS时序函数之后,你将会真正体验到竖起头发般的...

    fobnn 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<