Event Loop 就是我们所熟知的事件轮询。
前面一篇文章中,简单的介绍了一下异步,但是并没有说异步是怎么实现的。因此这篇文章主要讲一下 JS 实现异步的具体解决方案 —— Event Loop。
先简单解释一下 Event Loop 实现异步的过程:
- 同步的代码直接执行
- 异步函数先放在异步队列中
- 待同步函数执行完毕,轮询执行异步队列的函数。
setTimeout(function() {
console.log(100)
})
console.log(200)
// 执行结果
// 200
// 100
上面的代码中在执行的时候,当遇到 setTimeout
之后,它会先将 setTimeout
的函数先放进去异步队列中先不执行,然后继续执行后面的同步函数,因此将上面的代码分为主进程跟异步队列两部分的话,会是下面的情况:
// 主进程
console.log(200)
// 异步队列
function() {
console.log(100)
}
当主进程中的同步代码都执行完毕之后,JS 引擎会看看异步队列中是否存在需要执行的函数,如果存在,则拿到到主进程中执行。所以这就是执行结果先输出 200,再输出 100 的原因。
再看看另外一个例子:
setTimeout(function() {
console.log(1)
}, 100)
setTimeout(function() {
console.log(2)
})
console.log(3)
// 执行结果
// 3
// 2
// 1
上面这个例子,有两个 setTimeout
,第一个有 100 毫秒延迟,第二个没有延迟。当执行代码的时候,碰到第一个 setTimeout 并不会立即放到异步队列中,而是会等到 100 毫秒之后,才会放到异步队列中。由于第二个 setTimeout 没有设置延迟,因此它会立即放到异步队列中。
因此拆分成主进程和异步队列的话,会是下面的样子:
// 主进程
console.log(3)
// 异步队列
// 立即被放入
function() {
console.log(2)
}
// 100ms 之后被放入
function() {
console.log(1)
}
当主进程中的 console.log(3)
被执行完毕之后,js 引擎会看一下异步队列中是否存在函数需要执行。
由于第二个 setTimeout 没有设置延迟,所以之前碰到这个 setTimeout
的时候,就已经将它的函数放到异步队列中了。因此在执行完主进程中的同步函数之后,JS 引擎会在异步队列中找到 function(){ console.log(2)}
这个方法,并将它放到主进程中去执行。
当 function(){ console.log(1)}
执行完毕之后,JS 引擎又去看异步队列中是否有函数需要执行。需要注意哦,第一个 setTimeout 是设置了 100 毫秒的延迟的,因此 JS 引擎是没那么快在异步队列中找到第一个 setTimeout 的函数的。
第一个 setTimeout 100 毫秒之后才将函数放到异步队列中,那此时 JS 引擎就会监视异步队列中是否有需要执行的代码。直到 100 毫秒之后,setTimeout 将函数放入到异步队列中,这时候 JS 引擎看到异步队列中有需要执行的函数,便将函数放到主进程中执行。
所以轮询轮询,为什么叫轮询呀,就是因为 JS 引擎会先查看异步队列中是否有需要执行的代码,有需要执行的就拿到主进程中去执行。执行完毕之后主线程没东西执行了,JS 引擎有去看看异步队列有没有东西要执行。如果发现有东西需要执行,JS 引擎就将它放到主进程中执行。这个过程是一直循环重复的,因此它才叫轮询嘛。
在前一个例子的基础上,再加上一个 Ajax 请求来分析一下:
$.ajax({
url: 'xxxxx',
success: function (result) {
console.log('a')
}
})
setTimeout(function() {
console.log('b')
}, 100)
setTimeout(function() {
console.log('c')
})
console.log('d')
由于这个例子只是比前面的例子多了一个 ajax 请求函数,因此重点分析一下 ajax 请求这部分。ajax 中的 success 是一个异步函数,当 ajax 请求成功之后,success 这个异步函数才会存放到异步队列中的。由于 ajax 到底什么时候请求成功这个是不知道的,因此到底是在 100 毫秒之前放到异步队列中还是在 100 毫秒之后放到异步队列中是不确定的,因此是先执行 success 异步函数还是先执行第二个 setTimeout 是不确定的。
因此执行上面的例子,就可能出现两种情况了:
- 第一种情况 ajax 在 100 毫秒之内请求成功
- 第二中情况 ajax 在 100 毫秒之后请求成功
对应的执行结果:
// 第一种情况 ajax 在 100 毫秒之内请求成功的执行结果
// d
// c
// a ajax 请求成功后的执行结果
// b 100 毫秒延迟的 setTimeout 函数执行结果
// 第二种情况 ajax 在 100 毫秒之后请求成功的执行结果
// d
// c
// b
// a