美文网首页
异步与事件循环

异步与事件循环

作者: moidream | 来源:发表于2019-04-19 16:26 被阅读0次

前言

在前端面试中经常会遇到代码执行顺序的问题,搞清楚异步与事件循环的机制,可以对此类问题一通百通。

一、术语

1.1 同步 synchronous

众所周知“JavaScript是单线程的”。JavaScript只有一个主线程,负责解释和和执行JavaScript代码。

JavaScript主线程会按顺序去执行执行环境栈内的代码。当遇到耗时较长的任务时就会产生阻塞,当遇到死循环时会直接卡死,无法继续执行。

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以,为了避免复杂性,从一诞生,JavaScript就是单线程。1

1.2 异步 asynchronous

由于CPU执行速度很快,IO操作较慢。所有JavaScript设计为对于IO操作或计时任务都交由浏览器的web APIs或 node的C/C++ APIs去处理。等到处理完毕后再交到主线程去处理后续任务。

1.3 任务队列 task queue

通常来说在编码过程中常常需要在异步操作处理完成后去执行对应任务。JavaScript会在异步操作完成后将对应的任务放到任务队列里面。等到主线程空闲时再去执行。

任务队列分为macrotasks和microtasks两种。

macrotasks

  • setTimeout
  • setInterval
  • setImmediate (node)
  • requestAnimationFrame (浏览器)
  • I/O
  • UI rendering (浏览器)

microtasks

  • process.nextTick (node)
  • Promises
  • Object.observe (已废弃)
  • MutationObserver(浏览器)

1.4 事件循环

事件循环决定了JavaScript的执行顺序。
事件循环的顺序为

  1. 从上往下执行JavaScript内的代码。
  2. 遇到macrotasks或microtasks方法后视不同环境交由浏览器的web APIs或 node的C/C++ APIs处理 2
  3. web APIs或 node的C/C++ APIs处理完成后将microtasks方法对应的任务推入到microtasks队列,将macrotasks方法对应的任务推入到macrotasks队列。
  4. 主程序执行完毕后,即执行环境栈只剩下全局执行环境时,开始执行microtasks队列内的全部任务。然后取macrotasks队列内的一个任务执行,再执行microtasks队列内的全部任务(如果有)。再取macrotasks队列内的下一个任务执行,再执行microtasks队列内的全部任务。
  5. 重复循环以上步骤。

二、 特殊情况

2.1 process.nextTick

  1. process.nextTick的回调函数会在同一次循环内的任何其他异步回调函数之前执行。
  2. process.nextTick回调函数内部调用的process.nextTick仍会在同一阶段执行。递归调用的process.nextTick会阻断事件循环,使其无法进入下一阶段。
process.nextTick(function() {
  console.log(111)
  process.nextTick(function() {
    console.log(222)
  })
})

setImmediate(function () {
  console.log(333)
})

// -> 111 222 333

2.2 setImmediate

setImmediate 和 setTimeout(function() {}, 0)很类似。
当这两者在非异步回调函数的执行环境内执行时,其执行先后顺序不确定。 在异步回调函数的执行环境内执行时setImmediate总会先于setTimeout(function() {}, 0)执行。

setImmediate(function () {
  console.log(111)
})
  
setTimeout(function () {
  console.log(222)
})

// 执行顺序不确定,结果可能是111 222或222 111

setTimeout(function test () {
  setImmediate(function () {
    console.log(111)
  })
  
  setTimeout(function () {
    console.log(222)
  })
}, 0)

// 执行顺序确定,总是 111 222

2.3 UI渲染时机

通过Javascript代码更新后的UI渲染会在microtasks之后,macrotasks之前进行。 3

<div id="div">
  begin
</div>

<script>
  div.onclick = function () {
    div.innerHTML = 'end';

    setTimeout(function() {
      alert(' ui 已经渲染完毕 ');
      console.log('timeout');
    }, 0);

    new Promise(function(resolve) {
      console.log('promise1');
      for (var i = 0; i < 1000; i++) {
        i == 99 && resolve();
      }
      console.log('promise2');
    }).then(function() {
      console.log('then1');
      alert(' ui 开始渲染 ');
    });

    console.log('global');
  }

  // 执行顺序为 promise1, promise2, global, then1, ui 开始渲染, div内文字变为end, ui 已经渲染完毕, timeout
</script>

三、 总结

经过多天的查询资料,基本搞清楚了事件循环的执行步骤,彻底掌握了这些异步代码的执行顺序问题。

但是在一些细节上仍留有疑惑,如process.nextTick的回调函数是先放入任务队列等待执行还是直接放入执行环境栈直接执行。关于setImmediate和setTimeout(function() {}, 0)执行顺序不确定的原因也未找到较好的解释。等以后找到资料具体理解之后再做补充。

下篇文章分析一下Promise,看看这个最初由社区实现的功能代码是如何写的。

四、 参考

[1] JavaScript 运行机制详解:再谈Event Loop
[2] 理解异步JavaScript-事件循环
[3] Javascript事件循环机制以及渲染引擎何时渲染UI

相关文章

  • 异步与事件循环

    前言 在前端面试中经常会遇到代码执行顺序的问题,搞清楚异步与事件循环的机制,可以对此类问题一通百通。 一、术语 1...

  • 2018-03-12

    python异步与协程 异步编程: 异步I/O selet/poll/epoll 事件循环 + 回调共享状态管理困...

  • Node事件循环和多进程

    nodejs事件循环与多进程 why 事件循环对于深入理解nodejs异步至关重要fs, net,http,eve...

  • Nodejs写出高性能代码并在代码层面将性能发挥到极致

    TODO阻塞,事件循环,异步

  • 异步编程

    同步与异步 同步:按代码顺序依次执行 异步:先执行同步代码,完成后再执行异步代码 事件循环与消息队列:当代码执行到...

  • 浅谈JavaScript事件循环与Vue的批量异步更新策略

    在介绍事件循环之前,首先要明确以下几个关键概念。事件循环,同步和异步任务,宏任务,微任务。 一.事件循环 事件循环...

  • nodejs深入学(5)异步编程

    前言 上一章讲解了node如何通过事件循环实现异步,包括与各种IO多路复用搭配实现的异步IO已经与IO无关的异步A...

  • Javascript同步、异步与事件循环

    Javascript是单线程的JS引擎中负责解释和执行JavaScript代码的线程只有一个,主线程处理AJAX请...

  • JS同步异步与事件循环

    文章序 理解JS的单线程和同步异步逻辑、事件循环(event loop)是学习JavaScript进阶的必经之路,...

  • js事件循环

    js 是单线程的, js 的异步事件就是依赖于事件循环机制 事件循环 首先,我们来解释下事件循环是个什么东西: 就...

网友评论

      本文标题:异步与事件循环

      本文链接:https://www.haomeiwen.com/subject/ryhcgqtx.html