JavaScript 事件循环

258 阅读3分钟

js是一门单线程的语言,不可能进行多线程编程,异步编程就是多线程编程一种模式,但是我们经常讲到js的异步编程,其实是伪异步,因为它是单线程的,也就是同步,只有前面的代码执行完才能执行下面的代码。所以要理解js中的异步理念,就需要了解js的运行核心--事件循环(Event loop)

为什么js会有异步呢

setTimeout(function(){
    //5秒之后执行程序
    
},5000)

我们想象一下,在同步的执行上面的代码,需要等待5秒才能执行定时器中的程序,然后在往下执行,在这5秒的过程中,浏览器没有任何反应,出现了阻塞,在用户体验上很不好。所以异步的模式就出现,为了解决浏览器非阻塞的运行。

单线程如何做到异步

js的任务分为同步异步两种,它们的处理方式也不同,同步任务是直接在主线程上排队执行,异步任务则会被放到事件队列中,若有多个异步任务则要在事件队列中排队等待,事件队列类似一个缓冲区,任务下一步会被移到调用栈,然后主线程执行调用栈的任务。

单线程是指js引擎中负责解析执行js代码的线程只有一个主线程,即每次只能做一件事,而我们知道一个ajax请求,主线程在等待它响应的同时是会去做其它事的,浏览器先在事件表注册ajax的回调函数,响应回来后回调函数被添加到任务队列中等待执行,不会造成线程阻塞,所以说js处理ajax请求的方式是异步的。

总而言之,检查调用栈是否为空,以及确定把哪个异步任务加入调用栈的这个过程就是事件循环,而js实现异步的核心就是事件循环。

一次事件循环的步骤包括:

  1. 主线程在执行代码的时候,遇到异步任务会将它添加到一个事件队列中(可以理解为一个数组),然后继续执行下面的代码,直到同步代码执行完,然后执行步骤2

  2. 检查事件队列是否为空,非空执行步骤3,为空则继续执行步骤2

  3. 取出事件队列中的第一个放到调用栈,然后主线程执行调用栈的任务,再执行步骤4

  4. 执行视图更新,然后回到步骤2

这就是事件循环

先看一段代码,理解一下:(面试题哦)

console.log('start')

setTimeout(function() {
  console.log('setTimeout')
}, 0)

Promise.resolve().then(function() {
  console.log('promise1')
}).then(function() {
  console.log('promise2')
})

console.log('end')

打印台输出的log顺序是什么?结合上面的步骤分析一下

Event Loop

最后的结果是:

start
end
promise1
pormise2
setTimeout

参考资料

Event Loop