概念

    事件轮询 (eventloop) 是"一个解决和处理外部事件时将它们转换为回调函数的调用的实体(entity)"

    JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。所有任务都需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
在这里插入图片描述

任务队列

    “ 任务队列 " 是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空," 任务队列 " 上第一位的事件就会自动进入主线程。

    所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

    同步任务指的是,在主线程上立即执行的任务

    异步任务指的是,不进入主线程、而进入 " 任务队列 "(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

    异步任务又分为宏任务和微任务

JS如何实现异步操作

    JS 的异步是通过回调函数实现的,即通过任务队列,在主线程执行完当前的任务栈(所有的同步操作),主线程空闲后轮询任务队列,并将任务队列中的任务(回调函数)取出来执行。

    " 回调函数 "(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

    虽然 JS 是单线程的,但是浏览器的内核是多线程的,在浏览器的内核中不同的异步操作由不同的浏览器内核模块调度执行,异步操作会将相关回调添加到任务队列中。而不同的异步操作添加到任务队列的时机也不同,如 onclick, setTimeout,ajax 处理的方式都不同,这些异步操作是由浏览器内核的 webcore 来执行的,webcore 包含上图中的3种 webAPI,分别是 DOM Binding、network、timer模块。

  • onclick 由浏览器内核的 DOM Binding 模块来处理,当事件触发的时候,回调函数会立即添加到任务队列中。
  • setTimeout 会由浏览器内核的 timer 模块来进行延时处理,当时间到达的时候,才会将回调函数添加到任务队列中。
  • ajax 则会由浏览器内核的 network 模块来处理,在网络请求完成返回之后,才将回调函数添加到任务队列中。

异步执行机制

  • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
  • 主线程之外,还存在一个 " 任务队列 "(task queue)。只要异步任务有了运行结果,就在 " 任务队列 " 之中放置一个事件。
  • 一旦 " 执行栈 " 中的所有同步任务执行完毕,系统就会读取 " 任务队列 ",看看里面有哪些事件。于是那些对应的异步任务结束等待状态,进入执行栈,开始执行。
  • 主线程不断重复上面的第三步(事件轮询)

JS中事件队列的优先级

  • 先执行同步代码再执行异步代码,先执行微任务再执行宏任务
  • setTimeout( fn, 0 ),它在 " 任务队列 " 的尾部添加一个事件,因此要等到同步任务和 " 任务队列 " 现有的事件都处理完,才会得到执行。
  • new Promise在实例化的过程中所执行的代码都是同步进行的,而then中的回调是异步执行的。在同步代码执行完成后才会去检查是否有异步任务完成,并执行对应的回调
console.log(1)
setTimeout(() => {
	console.log(2)
}, 0)
new Promise((resolve, reject) => {
    console.log(3)
    setTimeout(() => {
        console.log(4)
    }, 0)
    resolve()
    console.log(5)
}).then((res) => {
    console.log(6)
})
console.log(7)

// 输出:1 3 5 7 6 2 4

宏任务

    由浏览器或Node.js环境发起。包括但不限于:script(整体代码)、setTimeoutsetIntervalsetImmediate(Node.js环境)、UI事件、I/O(Node.js)、UI render等。

微任务

    由JavaScript引擎发起。包括但不限于:PromiseMutationObserverprocess.nextTick(Node.js环境)。
    在微任务中,process.nextTick的优先级高于Promise

执行顺序

    执行栈的入口为script,而全局任务为宏任务,所以当执行栈为空的时候,同步任务执行完毕会先执行微任务队列,微任务执行完毕再读取宏任务队列中最前的任务,执行宏任务的过程中,如果遇到微任务则依次加入微任务队列,执行栈为空后再次读取微任务,依次类推

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐