Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

时间的魔术,Promise 的 then 执行完后再执行?

  JavaScript 的执行帧是个很神奇的东西,搞懂它我们就可以玩出很多时间的魔术。比如我之前写一个库时就有一个奇怪的需求,一个函数返回一个 Promise 对象给用户,函数内希望等到用户挂在这个 Promise 对象上的所有 then 执行完毕后再执行其他代码。
  也许我用文字描述的太含糊了?那么些段简单的代码吧(我说的是 JavaScript,不会有 setTimeout 这样 JavaScript 语言之外的东西。另外,此处用了 ES6): var foo = () => {
  var promise = Promise.resolve();
  // 此处插入代码,希望在用户挂载的 then 执行完成后再执行
  return promise;
};

foo().then(() => {
  console.log('这里是执行用户挂载的 then');
});
  如果了解 JavaScript 的执行帧,那么以上问题就可以解决。但要注意能解决的仅仅是挂载在 foo 函数直接返回的那个 Promise 对象上的 then。如果继续链式调用下去,在逻辑上也是无法解决的。先看代码:
var foo = () => {
  var promise = Promise.resolve();
   promise.then(() => {
     Promise.resolve().then(() => {
       console.log('在用户挂载的 then 之后执行');
     });
  });
  return promise;
};

foo().then(() => {
  console.log('这里是执行用户挂载的 then');
});
  上面的代码中 foo 函数内的 promise.then 和 foo().then 是在同一个执行帧中调用的,所以当这个 promise 对象 resolve 后它们是按照注册的顺序先后执行的(先执行 promise.then 回调,然后执行 foo().then 回调)。也就是说,foo 函数内直接挂一个 then 肯定比外面的 then 先执行。但是由于 foo 函数里面的 then 还有一层异步,所以在那个 promise 被 resolve 后,第二个和第三个执行帧的代码如果平行地拿出来应该是这样: Promise.resolve().then(() => {
  console.log('在用户挂载的 then 之后执行');
});

console.log('这里是执行用户挂载的 then');
  这个代码会先输出哪一个就完全没悬念了吧?我们只要把执行帧一层层的解剖出来看就很容易看明白这些代码执行的先后顺序。
  但是我前面也说了,如果想在链式调用的 then 中的最后一层处理完之后再执行一个函数这件事是逻辑上做不到的,除非劫持 then 方法。而且上面我演示的代码可以实现这个效果是基于两个 then 在同一个执行帧中绑定的。实际上用户可能异步地绑定 then,这时候我们同样无法实现先前的效果。比如: var foo = () => {
  var promise = Promise.resolve();
   promise.then(() => {
     Promise.resolve().then(() => {
       console.log('这个代码就没法在用户异步绑定的 then 之后执行');
     });
  });
  return promise;
};

var result = foo();
Promise.resolve().then(() => {
  result.then(() => {
    console.log('这里是用户异步绑定的 then');
  });
});
  这些执行顺序虽然有规律可循,但有时候会很复杂。我自己也经常被一堆异步的执行顺序搞晕。也许这算是 Promise 的坑吧?或者是我用它的姿势不对呢?
网名:
34.203.245.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^