关于promise的执行顺序的问题,恳请哪位大佬帮我解决一下

关于promise原型上的then()方法的执行顺序的问题

  • 为什么是这样的执行顺序,希望能够详细解释下

源代码出处

Promise.resolve("foo")
  .then(function(string) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        string += 'bar';
        resolve(string);
      }, 1);
    });
  })
  .then(function(string) {
    setTimeout(function() {
      string += 'baz';
      console.log(string);
    }, 1)
    return string;
  })
  .then(function(string) {
    console.log("sssss");
    console.log(string);
  });

// 执行顺序
// ssss
// foobar
// foobarbaz

问题出现的环境背景及自己尝试过哪些方法

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

你期待的结果是什么?实际看到的错误信息又是什么?

阅读 2.5k
4 个回答

这里涉及到event loop里面的宏任务与微任务
如果不了解可以先看看这篇文章:https://juejin.im/post/5b498d...

了解完后我们现在应该能知道: promise.then属于微任务,setTimeout属于宏任务

下面开始解答:

第一步:

// Promise.resolve是同步的,首先当然是先执行这个同步任务
Promise.resolve("foo")

// 根据代码可以看到有三个promise.then(暂且叫他们then1、then2、then3)
// 以上三个then会依次进入microtask(微任务)栈中, 首先进入的是then1
Promise.resolve("foo")
  .then(function(string) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        string += 'bar';
        resolve(string);
      }, 1);
    });
  })
 // 因为then1中的resovle仍未执行, 所以then2、then3暂时不会进入microtask(微任务)中
 // 至此:macrotask(宏任务):空,microtask:then1

第二步:

// 第一步执行完后,同步任务执行完毕
// 接着开始寻找microtask(微任务),也就是上面的 then1
// then1 return了一个promise,这里是同步执行的,到达setTimeout后将setTimeout放入macrotask
// 至此:macrotask:set1,microtask:空
  .then(function(string) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        string += 'bar';
        resolve(string);
      }, 1);
    });
  })

第三步:

// 微任务执行完毕后就执行宏任务,也就是set1
  setTimeout(function() {
    string += 'bar';
    resolve(string);
  }, 1);
// 1毫秒后,string变为foobar, resolve执行完毕,开始处理then2
// 至此:macrotask:空,microtask:then2

第四步:

// 执行then2
  .then(function(string) {
    // setTimeout(暂且叫set2)放入宏任务然后跳过
    setTimeout(function() {
      string += 'baz';
      console.log(string);
    }, 1)
    // return是同步任务,不用等待
    return string;
  })
// 到此then2执行完毕,因为then2里面有return,因此then3进入microtask
// 至此:macrotask:set2, microtask: then3

第五步:

// 同步任务执行完毕后开始寻找 microtask,也就是then3
  .then(function(string) {
    console.log("sssss");
    console.log(string);
  });
// then3里面都是同步任务所以输出
// ssss
// foobar(还记得set1执行完后变成了foobar吗)
// 至此:macrotask:set2, microtask: 空

第六步:

// 最后执行set2
    setTimeout(function() {
      string += 'baz';
      console.log(string);
    }, 1)
// string变为foobarbaz
// 输出 string

因此输出顺序为:
sssss
foobar
foobarbaz

问题答得不多,所以语言组织方面可能不太通顺,望见谅,希望我的解答能帮到你

Promise 怎么用的就不解释了。

首先代码可以拆解为三部分:

const sep01 = Promise.resolve("foo")
  .then(function(string) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        string += 'bar';
        resolve(string);
      }, 1);
    });
  })
  
const sep02 = sep01.then(function(string) {
    setTimeout(function() {
      string += 'baz';
      console.log(string);
    }, 1)
    return string;
  })
  
sep02.then(function(string) {
    console.log("sssss");
    console.log(string);
  });
  
// sssss
// foobar
// foobarbaz

解析:第一个.then

const sep01 = Promise.resolve("foo")
  .then(function(string) {
    // 这里.then先执行
    return new Promise(function(resolve, reject) {
      // promise 内部代码也是立即执行的
      setTimeout(function() {
        string += 'bar'; // 'foobar'
        // 记住这里,返回的是  foobar
        resolve(string);
      }, 1);
    });
  })
console.log(sep01) // 这里值是 return返回的 new Promise

第二个.then

const sep02 = sep01.then(function(string) {
    // 这里的 string 值是 foobar,这不是引用类型,也就是说后续改成啥了,这里的string还是是 foobar
    setTimeout(function() {
      // 异步代码,添加到宏任务里,后续执行
      string += 'baz'; // foobarbaz 
      console.log(string); // foobarbaz
    }, 1) // 这里1改为0也是一样结果的
    return string; // 返回的是 foobar
  })

第三个.then

sep02.then(function(string) {
    // 这里string是上个.then返回的值,foobar
    console.log("sssss");
    console.log(string); // foobar
  });

eventloop了解一下
字段内存存储了解一下

首先你的 resolve(string) 写在了promise的setTimeout里面,所以你的then里面的回调函数都会在

setTimeout(function() {
       string += 'bar';
       resolve(string);
     }, 1);

resolve(string) 之后执行,此时string的值已经是foobar,然后两个then,setTimeout的会在下一条队列执行,而then会在当前队列的末尾执行,所以就会先打印 foobar ,再打印 foobarbaz

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题