内容简介:上一篇中@TDGarden说:又到了春暖花开、万物复苏的季节,大家都忙着谈恋爱,没时间写博客了。然后在群里发了一张图给我:说这个位置适合我。
上一篇中@TDGarden说:又到了春暖花开、万物复苏的季节,大家都忙着谈恋爱,没时间写博客了。
然后在群里发了一张图给我:
说这个位置适合我。
于是我就懂了。我该写博客了。毕竟我们前端现在只有我有时间写博客。
好了,不瞎扯了,我们进入正题,来聊聊Event Loop。本文算是对这几天来学习Event Loop的总结和梳理,参考了很多大佬的文章,如有错误,恳请指正。
从单线程说起
众所周知,js是一种单线程语言。为什么是单线程呢?我引用一句烂大街的话:假设js同时有两个线程,一个线程想要在某个dom节点上增加内容,另一个线程想要删除这个节点,这时要以哪个为准呢?当然,多线程有多线程的解决办法,加锁啊,但是这样的话,又会引入锁、状态同步等问题。
js是浏览器脚本语言,主要用途是与用户互动,操作dom,多线程会带来很复杂的同步问题。
好吧,那就单线程吧。但是单线程又带来了单线程的问题,只有一个线程啊,任务要排队执行,如果前一个任务执行时间很长(ajax请求后台数据),后面的任务就都得等着。
Event Loop就出现了,来背单线程的锅。
Event Loop
往下看之前你应该知道栈、队列、同步任务、异步任务、执行栈这些基本概念。
关于执行栈有一篇很详细的文章推荐: JavaScript深入之执行上下文栈
请看下图:
-
js在执行代码时,代码首先进入执行栈,代码中可能包含一些同步任务和异步任务。
-
同步任务立即执行,执行完出栈,over。
-
异步任务也就是常见的
ajax
请求、setTimeout
等,代码调用到这些api的时候,WebAPIs来处理这些问题,执行栈继续执行。 -
异步任务有了运行结果时,(当
ajax
请求结果返回时),WebAPIs把对应的回调函数放到任务队列。 -
执行栈为空时来读取任务队列中的第一个函数,压入执行栈。
步骤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.nextTick
、 promise
吧,这时候执行顺序会有点儿复杂,往下看。
微任务、宏任务与Event-Loop 用了很通俗的例子讲了宏任务和微任务的区别,我这里就不啰嗦了。如果你不想了解也没关系,因为常见的宏任务、微任务就那几种,记住就可以了。
- 常见的宏任务:
script(整体代码)
、setTimeout
、setInterval
、I/O
、setImmedidate
- 常见的微任务:
process.nextTick
、MutationObserver
、Promise.then catch finally
process.nextTick
和 setImmidate
是只支持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
结语
如果看完本文你还是没太懂,那我建议你可以多看几篇文章,一个烧饼吃不饱,十个就差不多了。
涉及到 promise
、 async/await
、 Node
和浏览器环境下事件循环的区别等问题本文没有细讲,但是这些知识会帮你更好地掌握Event Loop。
看完 promise
可以做一下题试试水: Eventloop不可怕,可怕的是遇上Promise 。
以上所述就是小编给大家介绍的《浅谈Event Loop》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
世界因你不同:李开复自传(纪念版)
李开复,范海涛 著作 / 中信出版社 / 2015-7-10 / 39.00
编辑推荐 1.李开复唯一一部描写全面生平事迹的传记:《世界因你不同:李开复自传》书中讲述了家庭教育培育的“天才少年”;学校教育塑造的“创新青年”,走入世界顶级大公司,苹果、微软、谷歌等亲历的风云内幕,岁月30载不懈奋斗、追求事业成功的辉煌历程。 2.娓娓道来、字字珠玑、可读性和故事性皆佳。李开复博士是青少年成长成才的励志偶像,年轻家长、学校教师阅读后也能从中得到感悟和启发。 3.......一起来看看 《世界因你不同:李开复自传(纪念版)》 这本书的介绍吧!