浅谈Event Loop

栏目: jQuery · 发布时间: 6年前

内容简介:上一篇中@TDGarden说:又到了春暖花开、万物复苏的季节,大家都忙着谈恋爱,没时间写博客了。然后在群里发了一张图给我:说这个位置适合我。

上一篇中@TDGarden说:又到了春暖花开、万物复苏的季节,大家都忙着谈恋爱,没时间写博客了。

然后在群里发了一张图给我:

浅谈Event Loop

说这个位置适合我。

于是我就懂了。我该写博客了。毕竟我们前端现在只有我有时间写博客。

好了,不瞎扯了,我们进入正题,来聊聊Event Loop。本文算是对这几天来学习Event Loop的总结和梳理,参考了很多大佬的文章,如有错误,恳请指正。

从单线程说起

众所周知,js是一种单线程语言。为什么是单线程呢?我引用一句烂大街的话:假设js同时有两个线程,一个线程想要在某个dom节点上增加内容,另一个线程想要删除这个节点,这时要以哪个为准呢?当然,多线程有多线程的解决办法,加锁啊,但是这样的话,又会引入锁、状态同步等问题。

js是浏览器脚本语言,主要用途是与用户互动,操作dom,多线程会带来很复杂的同步问题。

好吧,那就单线程吧。但是单线程又带来了单线程的问题,只有一个线程啊,任务要排队执行,如果前一个任务执行时间很长(ajax请求后台数据),后面的任务就都得等着。

Event Loop就出现了,来背单线程的锅。

Event Loop

往下看之前你应该知道栈、队列、同步任务、异步任务、执行栈这些基本概念。

关于执行栈有一篇很详细的文章推荐: JavaScript深入之执行上下文栈

请看下图:

浅谈Event Loop
  1. js在执行代码时,代码首先进入执行栈,代码中可能包含一些同步任务和异步任务。

  2. 同步任务立即执行,执行完出栈,over。

  3. 异步任务也就是常见的 ajax 请求、 setTimeout 等,代码调用到这些api的时候,WebAPIs来处理这些问题,执行栈继续执行。

  4. 异步任务有了运行结果时,(当 ajax 请求结果返回时),WebAPIs把对应的回调函数放到任务队列。

  5. 执行栈为空时来读取任务队列中的第一个函数,压入执行栈。

步骤5不断重复,执行栈为空时,系统就去任务队列中拿第一个函数压入栈继续执行。这个过程不断重复,这就是事件循环(Event Loop)。

来看一个简单的demo。

console.log(1);

setTimeout(() => {
    console.log(2);
}, 2000);

console.log(3);
复制代码
  • console.log(1) 同步任务,输出1
  • setTimeout 异步任务,交给webapis去处理,2s后, console.log(2) 进入任务队列
  • console.log(3) 同步任务,输出3
  • 执行栈为空,系统读取任务队列里的事件
  • 执行 console,log(2) ,输出2

宏任务&微任务

说到这儿当然还没完。相信你肯定见过 process.nextTickpromise 吧,这时候执行顺序会有点儿复杂,往下看。

微任务、宏任务与Event-Loop 用了很通俗的例子讲了宏任务和微任务的区别,我这里就不啰嗦了。如果你不想了解也没关系,因为常见的宏任务、微任务就那几种,记住就可以了。

  • 常见的宏任务: script(整体代码)setTimeoutsetIntervalI/OsetImmedidate
  • 常见的微任务: process.nextTickMutationObserverPromise.then catch finally

process.nextTicksetImmidate 是只支持Node环境的。

还有, process.nextTick 是有一个插队操作的,就是说他进入微任务队列时,会插到除了 process.nextTick 其他的微任务前面。

所以,我们上面提到的任务队列,是包括一个宏任务队列和一个微任务队列的。每次执行栈为空的时候,系统会优先处理微任务队列,处理完微任务队列里的所有任务,再去处理宏任务。

做两道题

前面叨叨了那么多,下面做两道题试试水吧。

new Promise(resolve => {
    resolve(1);
    Promise.resolve().then(() => console.log(2));
    console.log(4);
}).then(t => console.log(t));
console.log(3);
复制代码

hahahhhhh我搬出了阮老师的题。

  • 首先 new Promise 执行, resolve(1) 表示创建的promise对象的状态变为resolved
  • Promise.resolve() 相当于创建了一个promise对象,then里面的匿名回调函数进入微任务队列,此时的微任务队列是 [() => console.log(2)]
  • 输出 4
  • new Promise 的then函数里面的匿名回调进入微任务队列, 此时的微任务队列是 [() => console.log(2), t => console.log(t)]
  • 输出 3

所以,最后输出的顺序是4 3 2 1。

emmmmmmm如果你不懂,那我觉得你可以先去复习一下promise。

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
  console.log('async2');
}
console.log('script start');
setTimeout(function() {
  console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
  console.log('promise1');
  resolve();
}).then(function() {
  console.log('promise2');
});
process.nextTick(() => {
  console.log('nextTick');
})
console.log('script end');
复制代码

这是一道烂大街的面试题。。我不信你没见过。话不多说,我们来分析emmmmm我建议你自己先做一下,再往下看。

看到 async/await 不必紧张,语法糖而已。 async 表示函数里有异步操作, await 之前的代码该怎么执行怎么执行, await 右侧表达式照常执行,后面的代码被阻塞掉,等待 await 的返回。返回是非 promise 对象时,执行后面的代码;返回 promise 对象时,等 promise 对象 resolved 时再执行。

所以可以理解成后面的代码放到了 promise.then 里面。

  • 输出 script start
  • WebAPIs在0s(哦,好像最短是4ms)之后把 setTimeout 里面的匿名回调函数丢进宏任务队列,简记为 ['setTimeout'] (请记得丢进任务队列里的是回调函数,函数!)
  • 输出 async1 start
  • 输出 async2
  • 要输出 async1 end 代码被丢进微任务队列,此时的微任务队列为 ['async1 end']
  • 输出 promise1
  • promise 对象状态变为 resolved
  • promise.then 里的匿名函数进入微任务队列,此时的微任务队列为 ['async1 end', 'promise2']
  • nextTick 插队到微任务队列对首, ['nextTick', 'async1 end', 'promise2']
  • 输出 script end
  • 执行栈空
  • 输出 nextTick
  • 输出 async1 end
  • 输出 promise2
  • 微任务队列为空
  • 输出 setTimeout

结语

如果看完本文你还是没太懂,那我建议你可以多看几篇文章,一个烧饼吃不饱,十个就差不多了。

涉及到 promiseasync/awaitNode 和浏览器环境下事件循环的区别等问题本文没有细讲,但是这些知识会帮你更好地掌握Event Loop。

看完 promise 可以做一下题试试水: Eventloop不可怕,可怕的是遇上Promise


以上所述就是小编给大家介绍的《浅谈Event Loop》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Refactoring

Refactoring

Martin Fowler、Kent Beck、John Brant、William Opdyke、Don Roberts / Addison-Wesley Professional / 1999-7-8 / USD 64.99

Refactoring is about improving the design of existing code. It is the process of changing a software system in such a way that it does not alter the external behavior of the code, yet improves its int......一起来看看 《Refactoring》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具