javascript 事件循环解析
用几张图来示意了解一下 事件循环和异步调用
先来个简单的 ajax 异步
让我们来看看下面的例子:
// ajax(..) is some arbitrary Ajax function given by a library
var response = ajax('https://example.com/api');
console.log(response); // => undefined
// `response` won't have the response
以上代码中 ajax 是异步调用的,所以你会看见 代码执行的时候Ajax(..)函数没法给response变量赋予真正的返回值。
如果你想让它正常运行如下:
ajax('https://example.com/api', function(response) {
console.log(response); // `response` is now available
});
开始我们的事件循环解析吧:
setTimeout(callback, milliseconds)
方法也可以实现异步。setTimeout函数所做的是设置一个事件(超时),以便稍后发生。让我们来看看:
function first() {
console.log('first');
}
function second() {
console.log('second');
}
function third() {
console.log('third');
}
first();
setTimeout(second, 1000); // Invoke `second` after 1000ms
third();
输出如下:
first
third
second
解析事件循环
先看一张图吧: 这些Web API是什么? 本质上,它们是您无法访问的线程,你可以对它们进行调用。它们是并行启动的浏览器的一部分。如果你是一个 Node.jS开发者,这些是C++API。
那什么又是事件循环呢?
事件循环有一个简单的工作——监视调用堆栈和回调队列。如果调用堆栈是空的,它将从队列中取出第一个事件,并将其推到调用堆栈,该堆栈有效地运行它。 这样的迭代被称为事件循环中的 一个标记 。每个事件都只是一个函数回调。
上面一堆废话后!让我们来具体看看到底里面 发生了什么鬼吧!
1. 状态是很清晰的. 浏览器控制台显示,调用堆栈是空的.
2. console.log(‘Hi’) 被添加到调用堆栈中.
3. console.log(‘Hi’) 执行.
4. console.log(‘Hi’) 从调用堆栈中删除.
#### 5. setTimeout(function cb1() { … }) 被添加到调用堆栈中.
#### 6. setTimeout(function cb1() { … }) 执行。浏览器将作为Web api的一部分创建一个计时器。它将为你处理倒计时。
#### 7. The setTimeout(function cb1() { … }) 它本身是完整的,并且从调用堆栈中删除。
#### 8. console.log(‘Bye’) 被添加到调用堆栈中.
#### 9. console.log(‘Bye’) 执行.
#### 10. console.log(‘Bye’) 从调用堆栈中删除.
11. 在至少 5000 ms 后, 计时器完成并将cb1回调推到回调队列。
12. 事件循环从回调队列中获取cb1,并将其推到调用堆栈。
13. cb1 执行 和添加 console.log(‘cb1’) 到调用堆栈.
14. console.log(‘cb1’) 执行.
15. console.log(‘cb1’) 从调用堆栈中删除.
16. cb1 从调用堆栈中删除.
最后来个快速回顾吧:
值得注意的是,ES6指定了事件循环应该如何工作,这意味着从技术上讲,它被加入到JS引擎的职责范围内,不再只是托管环境中的一部分。造成这种变化的一个主要原因是在ES6中引入了Promise,因为后者需要对事件循环队列的调度操作进行直接的、细粒度的控制。
setTimeout(…) 是怎么样工作的
需要注意的是setTimeout(…)不会自动将您的回调放到事件循环队列中。它设置了一个计时器。
托管环境将您的回调放入事件循环中,以便将来的某个时间点将会接收并执行它。看看这段代码:
setTimeout(myCallback, 1000);
这并不意味着myCallback 将在1000毫秒时执行,而是在1000毫秒时,myCallback将被添加到队列中。不过,队列可能会有其他之前添加的事件——你的回调将不得不等待。
那么 setTimeout(callback, 0)的工作是什么呢?
现在你知道事件循环做了什么事情,以及setTimeout是如何工作的:调用setTimeout,并将0作为第二个参数只是推迟回调,直到调用堆栈为空。
console.log('Hi');
setTimeout(function() {
console.log('callback');
}, 0);
console.log('Bye');
尽管等待时间设置为0毫秒,但浏览器控制台的结果如下:
Hi
Bye
callback