JavaScript中,如何理解事件这一概念?
Javascript Event Loop
原文链接:https://dev.to/ejjraifihamza/javascript-event-loop-3kd4
Javascript 单线程模式
每个人都知道,JavaScript是单线程编程语言,换句话说JavaScript只能在一个时间点做一件事情。
JavaScript引擎从文件的最顶部依次执行每一条script语句,在执行过程中,JavaScript制造了执行上下文,并且push 和pop函数进出调用栈。
如果一个函数花费了很长的时间去执行,那么在这个函数执行的时间内你无法与页面进行交互,因为页面被挂起了。
一个花费很长时间去执行完成的函数被叫做阻塞函数(blocking function)。技术上来讲,一个阻塞函数阻塞了所有的页面交互,比如鼠标点击。
一个阻塞函数可以是从远程服务器去下载一个文件的函数或者是从外部调用API。
Example of blocking function
function task(message) {
// emulate time consuming task
let n = 10000000000;
while (n > 0){
n--;
}
console.log(message);
}
console.log('Start script...');
task('Download a file.');
console.log('Done!');
所以我们这里有的是在take()函数中有一个很大的while循环来模拟一个耗时的任务,这个take()就是阻塞函数,为什么?因为它花费了很长的时间去完成。
因此,脚本会挂起几秒钟(取决于计算机有多快),然后发出以下输出。
Start script...
Download a file.
Done!
为了执行这个脚本,JavaScript引擎将第一个调用的console.log()放到调用栈的最顶端,并执行它,然后JavaScript将task()放到调用栈的最顶端并且执行它。
然而,它(JavaScript引擎)花费一段时间将take()执行完成,因此,你将会在一段时间之后看见信息Download a file ,当take()执行完成后,JavaScript引擎将它弹出调用栈。
最后,JavaScript引擎将执行非常快的console.log('Done')放到引擎中去执行。(原文这里写的可能有点错误,我自己修改了一下)。
下图说明了这一点

Callbacks to the rescue
为了阻止阻塞函数阻塞其他活动,你通常将它放到回调函数中,为了之后执行它。
function task(message) {
// emulate time consuming task
let n = 10000000000;
while (n > 0){
n--;
}
console.log(message);
}
console.log('Start script...');
setTimeout(() => {
task('Download a file.');
}, 1000);
console.log('Done!');
在这个例子中,你将会立刻看见Start script...和Done,在这之后你将会看到信息Downloadn a file。
这是输出:
Start script...
Done!
Download a file.
当你调用setTimeout()函数的时候,去发送一个请求,或者是点击一个按钮,浏览器可以并发和异步的去执行这些活动。
setTimeout(),获取数据请求和DOM时间都是web浏览器的webs API的一部分。
在我们的例子中,当我们调用setTimeout函数的时候,JavaScript引擎将它放到调用栈中,并且Web API创造一个在一秒后过期的定时器。
然后JavaScript引擎将take()函数放到一个叫做调用队列或者叫做任务队列中去。
下图说明了这一点

这个event loop是一个不断运行的程序,它是监视回调队列和调用栈。
event loop在将task()从回调队列中移出的时候,首先问一下调用栈是否为空,如果不是event loop 会一直等到它为空,然而,如果为空,它会将task()移动到调用栈中。
这就是事件循环,下一篇博客是关于提升的。
Summary
在这个博客中,你学习到了关于JavaScript event loop的知识,一个不断运行的程序,它协调调用栈和任务队列之间的任务来实现并发
