本文首发于掘金@黑金团队,现搬运到本身账号html
上一篇中@TDGarden说:又到了春暖花开、万物复苏的季节,你们都忙着谈恋爱,没时间写博客了。
而后在群里发了一张图给我:前端
好了,不瞎扯了,咱们进入正题,来聊聊Event Loop。本文算是对这几天来学习Event Loop的总结和梳理,参考了不少大佬的文章,若有错误,恳请指正。es6
众所周知,js是一种单线程语言。为何是单线程呢?我引用一句烂大街的话:假设js同时有两个线程,一个线程想要在某个dom节点上增长内容,另外一个线程想要删除这个节点,这时要以哪一个为准呢?固然,多线程有多线程的解决办法,加锁啊,可是这样的话,又会引入锁、状态同步等问题。web
js是浏览器脚本语言,主要用途是与用户互动,操做dom,多线程会带来很复杂的同步问题。面试
好吧,那就单线程吧。可是单线程又带来了单线程的问题,只有一个线程啊,任务要排队执行,若是前一个任务执行时间很长(ajax请求后台数据),后面的任务就都得等着。ajax
Event Loop就出现了,来背单线程的锅。api
往下看以前你应该知道栈、队列、同步任务、异步任务、执行栈这些基本概念。promise
关于执行栈有一篇很详细的文章推荐:JavaScript深刻之执行上下文栈浏览器
请看下图: bash
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)
同步任务,输出1setTimeout
异步任务,交给webapis去处理,2s后,console.log(2)
进入任务队列console.log(3)
同步任务,输出3console,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对象的状态变为resolvedPromise.resolve()
至关于建立了一个promise对象,then里面的匿名回调函数进入微任务队列,此时的微任务队列是[() => console.log(2)]
new Promise
的then函数里面的匿名回调进入微任务队列, 此时的微任务队列是[() => console.log(2), t => console.log(t)]
因此,最后输出的顺序是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
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。
以上,若有错漏,恳请指正!
安利一下个人公众号。 (非技术号)这里是糖糖的文字,有社会现象的理性分析,也会有柴米油盐的生活温情。我是会打代码的生活号主,欢迎你来。