你可能不了解的 Promise 微任务类型

更新日期: 2022-05-27阅读: 915标签: Promise

一道示例题引发的知识盲区

// promise1
new Promise(resolve => {
  resolve(
    new Promise(resolve => {
      resolve(1);
    })
  );
}).then(res => {
  console.log('1');
});

new Promise(resolve => {
  resolve(2);
})
  .then(() => {
    console.log('2');
  })
  .then(() => {
    console.log('3');
  })
  .then(() => {
    console.log('4');
  });

// 输出顺序:
// 2
// 3
// 1
// 4

先来看一道示例题。按照以往的理解,我以为输出顺序是 2 1 3 4。然后通过调试发现 promise1 在初始化后状态依然是 pending,感觉自己在理解 Promise 微任务方面还是存在不足。研究了下 ECMA 规范,终于把这个问题搞清楚了。


微任务的类型

ECMA 规范中把 Promise 微任务分成了两种类型,下面结合规范来分别看下这两种微任务的产生时机和执行内容。

NewPromisereactionJob

当 Promise 决议后执行 then() 中注册的回调时,或当 then() 注册时 Promise 已决议,会产生这种微任务。为简化说明,我们先关注这个场景:当前 Promise 已经决议,接着调用了 then(),比如这样:

Promise.resolve(1).then(res => console.log(res));

res => console.log(res) 就运行在这种微任务中。来看规范对于 Promise.prototype.then 的描述:


因为 Promise 已经是 fulfilled 状态,我们接着看 PerformPromiseThen 中对于 fulfilled 状态的操作:


这里就是创建了一个 NewPromiseReactionJob 微任务,并加入到了微任务队列中。我们再看看 NewPromiseReactionJob 里面是怎么执行的:


该微任务主要包含两个内容:

  1. 执行 handler,handler 就是 then() 中注册的回调,得到返回结果。
  2. 对 then() 中产生的新 Promise 执行 resolve(返回结果) 或 reject(返回结果)。

NewPromiseResolveThenableJob

上面那种微任务基本是大家熟知的情况,这个微任务类型就是示例题中提到的盲区了。首先注意到 resolve 函数的描述:


如果一个对象的 then 属性可以被调用(是一个函数),那么这个对象就是 thenable 对象。调用 resolve() 传递的参数值如果是一个 thenable 对象,就会产生 NewPromiseResolveThenableJob 这种微任务了。接下来看看这个微任务的内容:


大概意思就是这种微任务产生了如下的代码

// resovle 和 reject 是调用 resolve(thenable) 时那个 Promise 上的。
thenable.then(resolve, reject); 

那么结合第一种微任务,如果 thenable 对象是 Promise,则这个微任务执行后又会产生第一个微任务。为什么要这样做呢?规范上有一段解释:

This Job uses the supplied thenable and its then method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the then method occurs after evaluation of any surrounding code has completed.

直接翻译的话大概就是说要等周围的同步代码执行完后才会执行这个。关于这个设计意图,我的理解是考虑到 thenable 对象的不一定是 Promise 实例,也可能是用户创建的任何对象;如果这个对象的 then 是同步方法,那么这样做就可以保证 then 的执行顺序也是在微任务中。


示例题分析

我们再来分析一下示例题:

// promise1
new Promise(resolve => {
  resolve(
    // promise2
    new Promise(resolve => {
      resolve(1);
    })
  );
}).then(res => {
  console.log('1');
});

// promise3
new Promise(resolve => {
  resolve(2);
})
  .then(() => {
    console.log('2');
  })
  .then(() => {
    console.log('3');
  })
  .then(() => {
    console.log('4');
  });

代码执行后,我们用伪代码来表示下微任务队列的内容:

const microTasks = [
  function job1() {
    promise2.then(promise1.[[Resolve]], promise1.[[Reject]]);
  },
  function job2() {
    const handler = () => {
      console.log('2');
    };
    
    // 决议 then() 返回的新 Promise。
    resolve(handler(2));
  }
];

接着开始执行微任务队列。job 1 执行后,产生了新的微任务 job 3:

const microTasks = [
  function job2() {
    const handler = () => {
      console.log('2');
    };
    resolve(handler(2));
  },
  function job3() {
    const handler = promise1.[[Resolve]];
    resolve(handler(1));
  }
];

job 2 执行后,输出了 2,并且产生新的微任务 job 4:

const microTasks = [
  function job3() {
    const handler = promise1.[[Resolve]];
    resolve(handler(1));
  },
  function job4() {
    const handler = () => {
      console.log('3');
    };
    resolve(handler(undefined));
  }
];

注意到 job 3 的内容是会让 promise1 决议,那么就会执行 promise1 的 then 回调,则会再产生一个微任务 job 5;并且 job 4 执行完后输出变为 2 3,并让 then() 产生的新 Promise 决议,也会再产生下一个的微任务 job 6:

const microTasks = [
  // job 5 由 job 3 产生。
  function job5() {
    const handler = () => {
      console.log('1');
    };
    resolve(handler(1));
  },
  function job6() {
    const handler = () => {
      console.log('4');
    };
    resolve(handler(undefined));
  }
];

那么最后的输出结果就是 2 3 1 4 啦,大家可以把以上分析方法放在其他的题目中验证康康是不是对的。

来源:小声比比 JavaScript

链接: https://www.fly63.com/article/detial/11602

你真的了解 Promise 吗?Promise 必知必会(十道题)

Promise 想必大家十分熟悉,想想就那么几个 api,可是你真的了解 Promise 吗?本文根据 Promise 的一些知识点总结了十道题,看看你能做对几道。

剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类

本文写给有一定Promise使用经验的人,如果你还没有使用过Promise,这篇文章可能不适合你,Promise标准中仅指定了Promise对象的then方法的行为,其它一切我们常见的方法/函数都并没有指定.

Async/Await替代Promise的6个理由

Async/Await替代Promise的6个理由:Async/Await是近年来JavaScript添加的最革命性的的特性之一。它会让你发现Promise的语法有多糟糕,而且提供了一个直观的替代方法。

Promise 原理解析与实现(遵循Promise/A+规范)

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一,Promise 是一个构造函数, new Promise 返回一个 promise对象 接收一个excutor执行函数作为参数

简单模仿实现 Promise 的异步模式

这篇文章是考虑如何自己实现一个简单 Promise,用以理解 Promise。和原生 Promise的调用方法一样,支持链式调用,本文实现的方法只能用于参考Promise的原理,还有很多特性没有实现,比如 race,all 方法的实现。

数组的遍历你都会用了,那Promise版本的呢

在对数组进行一些遍历操作时,发现有些遍历方法对Promise的反馈并不是我们想要的结果。async/await为Promise的语法糖,文中会直接使用async/await替换Promise;map可以说是对Promise最友好的一个函数了,

Promise使用时应注意的问题

最近在使用axios库时遇到了个问题,后端接口报了500错误,但前端并未捕获到。在axios整体配置的代码中,过滤http code时,调用了filter401()、filter500(),但是这里注意并未将两个filter函数的结果返回,也就是并未返回promise,这就是导致问题出现的原因

es6 Promise 的基础用法

想必接触过Node的人都知道,Node是以异步(Async)回调著称的,其异步性提高了程序的执行效率,但同时也减少了程序的可读性。如果我们有几个异步操作,并且后一个操作需要前一个操作返回的数据才能执行

关于 Promise 的 9 个提示

你可以在 .then 里面 return 一个 Promise,每次执行 .then 的时候都会自动创建一个新的 Promise,对调用者来说,Promise 的 resolved/rejected 状态是唯一的,Promise 构造函数不是解决方案,使用 Promise.resolve

手写一款符合Promise/A+规范的Promise

Promise的一些用法在此不多赘述,本篇主要带领你手写一个Promise源码,学完你就会发现:Promise没有你想象中的那么难.本篇大概分为以下步骤:实现简单的同步Promise、增加异步功能、增加链式调用then、增加catch finally方法、增加all race 等方法、实现一个promise的延迟对象defer、最终测试

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!