JavaScript作业队列和微任务

来都来了 2020-10-16 11:03:03 ⋅ 983 阅读

JavaScript作业队列和微任务

当Promises在ES6中首次引入时,它们使编写异步代码的工作变得更加容易。回调地狱被更简单的构造所取代,该构造使开发人员可以更轻松地处理异步任务。理解诺言的关键是知道作业队列(也称为微任务队列)如何在JavaScript中工作。

我们将从看一些代码开始:

function firstFunction() {
  thirdFunction()

  const firstResponse = Promise.resolve('1st Promise');
  const secondResponse = Promise.resolve('2nd Promise');

  setTimeout(() => {
    firstResponse.then(res=> {
      console.log(res);
    })
  })

  secondResponse.then(res=> {
    console.log(res);
  })
}

function thirdFunction() {
  const thirdResponse = Promise.resolve('3rd Promise');
  const fourthResponse = Promise.resolve('4th Promise');

  queueMicrotask(() => {
    console.log('Hello from the microtask queue')
  })

  thirdResponse.then(res=> {
    console.log(res);
  })

  setTimeout(() => {
    fourthResponse.then(res=> {
      console.log(res);
    })
  })
}

function secondFunction() {
  let i = 0;
  let start = Date.now();

  for (let j = 0; j < 5.e9; j++) {
    i++;
  }
  console.log("Loop done in " + (Date.now() - start) + 'ms');
}

setTimeout(() => {
  console.log('first timeout')
});

firstFunction()
secondFunction()
console.log('first console log')

我们期望日志以什么顺序出现?

进入事件循环

得知ECMAScript规范未提及事件循环,可能令人惊讶。相反,事件循环是指浏览器的JavaScript引擎处理代码的方式。JavaScript在单线程模型上运行,因此在任何时候都只能处理一项任务。这显然会导致并发症。如果mouseover在计时器启动的计时器即将setTimeout到期之前触发事件,会发生什么情况?或者,如果您触发了网络请求,并且响应出现在浏览器中间,则重新呈现了UI?

下图显示了浏览器的不同部分,它们可以协同工作来管理这种异步性。

典型Web浏览器的内部

事件循环以迭代或“滴答”的形式执行其工作。JavaScript代码以运行到完成的方式执行(当前任务总是在下一个任务执行之前完成),因此,每次任务完成时,事件循环都会检查事件是否将控制权还给其他代码。如果不是,它将运行作业队列中的所有任务,然后运行任务队列中的任务。我们可以通过将其应用于示例代码来更好地说明这一点。

// ... firstFunction, secondFunction and thirdFunction declarations have been omitted for brevity

setTimeout(() => {
  console.log('first timeout')
});

firstFunction()
secondFunction()
console.log('first console log')

firstFunction执行时,浏览器的内部状态为:

替代文字

如果setTimeout在没有指定持续时间的情况下调用,则默认为0毫秒。setTimeout本身是一个浏览器API,因此它不会出现在调用堆栈中,但它返回的回调将放入任务队列中,以备将来事件循环迭代时调用。

首先要做的firstFunction是call thirdFunction,它看起来像:

function thirdFunction() {
  const thirdResponse = Promise.resolve('3rd Promise');
  const fourthResponse = Promise.resolve('4th Promise');

  queueMicrotask(() => {
    console.log('Hello from the microtask queue')
  })

  thirdResponse.then(res=> {
    console.log(res);
  })

  setTimeout(() => {
    fourthResponse.then(res=> {
      console.log(res);
    })
  })
}

这就是事情变得有趣的地方。在上面的代码中,我们解析了两个promise,并为其分配了解析值。使用then每个promise的方法,我们指定应在结算后运行的函数。已解决的承诺是从pending(在执行诸如获取数据之类的基础过程时)迁移到fulfilled(成功)或rejected(错误)的承诺。稳定后,它将微任务排队以进行回调。queueMicrotask是不言自明的。这是排队微任务的更直接方法。

一旦thirdFunction完成执行,控制权将交还给firstFunction它,同时完成其代码的运行。之后,浏览器的内部状态为:

替代文字

值得注意的是,尽管运行了两个函数,我们的程序在这一点上还没有做任何事情。当使用异步代码时,这些细微差别可能会使开发人员感到困惑,尤其是当您考虑到我们的代码的下一行secondFunction通过运行持续几秒钟的循环来模仿阻塞代码的行为时。尽管队列中有六个回调,但是console.login语句secondFunction是打印到控制台的第一件事,其后是脚本的最后一行,这是另一条日志语句。

在此阶段,事件循环到达其当前迭代的末尾,因此它查找作业队列并以先进先出的方式在该队列中运行回调。作业队列中的代码有可能安排更多的回调。但是,这些不会推迟到以后的迭代中,而是会在当前迭代中运行,这意味着可以通过创建作业队列回调的无穷循环来使程序饿死。在第一次迭代结束时,以下内容将被记录到控制台:

Loop done in 5672ms
first console log
Hello from the microtask queue
3rd Promise
2nd Promise

浏览器的内部状态为:

替代文字

您会注意到,第一个和第四个Promise的回调从未放入作业队列。这是因为我们没有then直接调用它们,而是将它们放在setTimeout函数的回调中。因此,当事件循环开始其第二次迭代时,它将首先查看任务队列。setTimeout我们的Promise的回调将每个排队另一个作业队列的回调,该回调将在当前迭代结束时运行。这意味着当我们的程序完成运行时,以下是将事物记录到控制台的顺序。如果第二次迭代中的任何代码在任务队列中排队了更多的东西,则它将在以后的迭代中运行。

Loop done in 5672ms
first console log
Hello from the microtask queue
3rd Promise
2nd Promise
first timeout
4th Promise
1st Promise

全部评论: 0

    我有话说:

    信小程序商城(九):信授权并实现个人中心页面页面

    实现商城的信授权并获取用户信息个人中心页面布局

    信小程序-Image的widthFix属性rpm尺寸的使用

    在做信小程序的商品详情页,商品的详情是图片集合,渲染完成后发现图片加载的很不自然

    PowerJob —强大的分布式任务调度与计算框架

    PowerJob让您轻松完成作业的调度与繁杂任务的分布式计算。

    软改进 Chromium 标签页恢复功能

      软正在为 Chromium 改进标签页恢复功能,以提升 Edge Chrome 的可靠性。 当 Chrome/Edge 遭遇意外关闭或崩溃时,再次启动后会提供恢复标签页及其会

    信小程序电商实战-首页(下)

    上一篇:信小程序电商实战-首页(上)好了,上一期我们把首页搜索、导航栏广告轮播给做完了,那么接下来会继续

    Node&RabbitMQ系列二 延迟|死信队列

      前提 目前项目中采用ts+eggjs结合的方式,针对定时任务,采用schedule,随着业务的增多,觉得缺点啥,可能就是缺消息队列吧。上一篇文章,针对rabbitmq的基本语法进行了

    「开源资讯」任务调度中间件PowerJob 3.3.0 发布

    PowerJob简介 PowerJob是全新一代分布式调度与计算框架,能让您轻松完成作业的调度与繁杂任务的分布式计算。 下载地址:https://gitee.com/KFCFans/PowerJob

    PowerJob v3.3.2 已经发布,分布式任务调度中间件

    PowerJob v3.3.2 已经发布,PowerJob 是全新一代分布式调度与计算框架,能让您轻松完成作业的调度与繁杂任务的分布式计算。 此版本更新内容包括: Features 支持控制台查看

    信小程序商城(七):动态API实现商品分类

    信小程序商品分类页面布局并且调用动态API获取数据并加载

    你的老板逼你上服务了吗?

    第一推动力,由于这些因素导致了软件架构思想相关技...

    信小程序抖音实战-首页(上)

    你也可以用信小程序编写一个抖音

    信小程序 - iconfont 图标字体

    你还在使用图片作为小程序的图标?大猪告诉大家如何在小程序上使用iconfont字体图标

    信小程序商城(二):电商首页轮播、分类导航新品特卖实现

    本案例所有模块都为https动态调用API数据服务获得

    信小程序实现商品数量加减

    这是一个用信小程序原生代码实现的数量加减demo,主要是用于商品购物车或者商品详情修改数量使用

    11 个Javascript机器学习库

    1. Brain.js Brain.js是一个Javascript库,用于替代(现在已弃用的)“ 脑 ”库的神经网络,该库可与Node.js一起使用或在浏览器中使用(注释计算),并为不同任务提供不同

    信小程序商城(十):用户收货地址管理

    布局收货地址列表新增收货地址页

    信小程序商城(五):动态API实现商品详情页(下)

    加入购物车悬浮框、商品数量、价格计算、收藏加入购物车功能开发