js 运行机制(含异步机制、同步任务、异步任务、宏任务、微任务、Event Loop)

简介: js 运行机制(含异步机制、同步任务、异步任务、宏任务、微任务、Event Loop)

js 的同步任务和异步任务

js代码可以分为两种任务:

  • 同步任务(synchronous)—— 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
  • 异步任务(asynchronous)—— 不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

异步任务又分为宏任务与微任务:

  • 宏任务(macrotask )
事件/函数 浏览器环境 Node.js环境
I/O ✅ ✅
setTimeout ✅ ✅
setInterval ✅ ✅
setImmediate ❌ ✅
requestAnimationFrame ✅ ❌
  • 微任务(microtask )
事件/函数 浏览器环境 Node.js环境
process.nextTick ❌ ✅
MutationObserver ✅ ❌
Promise.then catch finally ✅ ✅

js在浏览器中的运行机制

从上到下依次解释执行所有代码

  • 遇到同步任务时,在主线程立刻执行该任务。
    此时主线程上有一个执行栈(execution context stack),所有同步代码会按顺序执行。
  • 遇到异步任务时,异步任务会进入到Event Table,当异步任务有结果后,将相对应的回调函数进行注册,放入事件队列(Event Queue);(异步的宏任务有结果后,会放入宏任务事件队列;异步的微任务有结果后,会放入微任务事件队列;)

(1)主线程从上到下依次执行所有同步任务

(2)主线程读取微任务事件队列,若存在微任务,则依次执行所有微任务

  • 微任务中同步任务会依次立刻执行
  • 微任务中微任务会依次添加到微任务事件队列末尾,待下一次读取微任务事件队列时执行
  • 微任务中宏任务会依次添加到宏任务事件队列末尾,待下一次读取宏任务事件队列时执行

(3)主线程读取宏任务事件队列,若存在宏任务,则依次执行所有宏任务

  • 宏任务中同步任务会依次立刻执行
  • 宏任务中微任务会依次添加到微任务事件队列末尾,待下一次读取微任务事件队列时执行
  • 宏任务中宏任务会依次添加到宏任务事件队列末尾,待下一次读取宏任务事件队列时执行

(4)依次重复第2步和第3步,直到清空微任务事件队列和宏任务事件队列

Event Loop(事件循环):主线程循环不断从"任务事件队列"中读取事件的运行机制称为Event Loop,这个过程是循环不断的,所以整个的这种运行机制

Event Loop 并不是在 ECMAScript 标准中定义的,而是在 HTML 标准中定义的

测试js在浏览器中运行机制的代码

console.log('0号同步任务1');
new Promise(function (resolve) {
    console.log('0号同步任务2');
    resolve();
}).then(function () {
    // 1号微任务
    setTimeout(function () {
        console.log('1号微任务中的宏任务1');
    })
    console.log('1号微任务中的同步任务1')
    new Promise(function (resolve) {
        console.log('1号微任务中的同步任务2');
        resolve();
    }).then(function () {
        // 1.1号微任务
        console.log('1.1号微任务中的同步任务1')
        setTimeout(function () {
            console.log('1.1号微任务中的宏任务1');
        })
        console.log('1.1号微任务中的同步任务2')
        setTimeout(function () {
            console.log('1.1号微任务中的宏任务2');
        })
    })
    console.log('1号微任务中的同步任务3')
    setTimeout(function () {
        console.log('1号微任务中的宏任务2');
    })
})
setTimeout(function () {
    console.log('0号宏任务中的同步任务1');
    new Promise(function (resolve) {
        console.log('0号宏任务中的同步任务2');
        resolve();
    }).then(function () {
        // 0号宏任务中的1号微任务
        setTimeout(function () {
            console.log('0号宏任务中的1号微任务中的宏任务1');
        })
        console.log('0号宏任务中的1号微任务中的同步任务1')
        new Promise(function (resolve) {
            console.log('0号宏任务中的1号微任务中的同步任务2');
            resolve();
        }).then(function () {
            //  0号宏任务中的1.1号微任务
            console.log('0号宏任务中的1.1号微任务中的同步任务1')
            setTimeout(function () {
                console.log('0号宏任务中的1.1号微任务中的宏任务1');
            })
        })
        console.log('0号宏任务中的1号微任务中的同步任务3')
    })
})
new Promise(function (resolve) {
    console.log('0号同步任务3');
    resolve();
}).then(function () {
    // 2号微任务
    setTimeout(function () {
        console.log('2号微任务中的宏任务1');
    })
    console.log('2号微任务中的同步任务1')
    new Promise(function (resolve) {
        console.log('2号微任务中的同步任务2');
        resolve();
    }).then(function () {
        // 2.1号微任务
        console.log('2.1号微任务中的同步任务1')
        setTimeout(function () {
            console.log('2.1号微任务中的宏任务1');
        })
    })
    console.log('2号微任务中的同步任务3')
})
console.log('0号同步任务4');

运行结果

0号同步任务1
0号同步任务2
0号同步任务3
0号同步任务4
1号微任务中的同步任务1
1号微任务中的同步任务2
1号微任务中的同步任务3
2号微任务中的同步任务1
2号微任务中的同步任务2
2号微任务中的同步任务3
1.1号微任务中的同步任务1
1.1号微任务中的同步任务2
2.1号微任务中的同步任务1
0号宏任务中的同步任务1
0号宏任务中的同步任务2
0号宏任务中的1号微任务中的同步任务1
0号宏任务中的1号微任务中的同步任务2
0号宏任务中的1号微任务中的同步任务3
0号宏任务中的1.1号微任务中的同步任务1
1号微任务中的宏任务1
1号微任务中的宏任务2
2号微任务中的宏任务1
1.1号微任务中的宏任务1
1.1号微任务中的宏任务2
2.1号微任务中的宏任务1
0号宏任务中的1号微任务中的宏任务1
0号宏任务中的1.1号微任务中的宏任务1


js在node.js中的运行机制

(1)V8引擎解析JavaScript脚本。

(2)解析后的代码,调用Node API。

(3)libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。

(4)V8引擎再将结果返回给用户。

Node.js还提供了另外两个与"任务队列"有关的方法:process.nextTick和setImmediate。

  • process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数。也就是说,它指定的任务总是发生在所有异步任务之前。【多个process.nextTick语句总是在当前"执行栈"一次执行完】
  • setImmediate方法则是在当前"任务队列"的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行。【多个setImmediate可能则需要多次loop才能执行完。】

由于process.nextTick指定的回调函数是在本次"事件循环"触发,而setImmediate指定的是在下次"事件循环"触发,所以很显然,前者总是比后者发生得早,而且执行效率也高(因为不用检查"任务队列")。

目录
相关文章
|
存储 JavaScript 前端开发
深入理解JavaScript中的事件循环(Event Loop):机制与实现
【10月更文挑战第12天】深入理解JavaScript中的事件循环(Event Loop):机制与实现
752 3
|
JavaScript 前端开发
Node.js 中实现多任务下载的并发控制策略
Node.js 中实现多任务下载的并发控制策略
676 15
|
Web App开发 JSON JavaScript
Node.js 中的中间件机制与 Express 应用
Node.js 中的中间件机制与 Express 应用
|
JavaScript 安全 中间件
深入浅出Node.js中间件机制
【10月更文挑战第36天】在探索Node.js的奥秘之旅中,中间件的概念如同魔法一般,它让复杂的请求处理变得优雅而高效。本文将带你领略这一机制的魅力,从概念到实践,一步步揭示如何利用中间件简化和增强你的应用。
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
2444 1
|
消息中间件 JavaScript 中间件
深入浅出Node.js中间件机制
【10月更文挑战第24天】在Node.js的世界里,中间件如同厨房中的调料,为后端服务增添风味。本文将带你走进Node.js的中间件机制,从基础概念到实际应用,一探究竟。通过生动的比喻和直观的代码示例,我们将一起解锁中间件的奥秘,让你轻松成为后端料理高手。
308 1
|
前端开发 JavaScript
深入理解JavaScript中的事件循环(Event Loop):从原理到实践
【10月更文挑战第12天】 深入理解JavaScript中的事件循环(Event Loop):从原理到实践
757 1
|
JavaScript 前端开发 开发者
原型链深入解析:JavaScript中的核心机制
【10月更文挑战第13天】原型链深入解析:JavaScript中的核心机制
680 0
|
JavaScript 前端开发 调度
在JavaScript中异步任务里的微任务和宏任务的特点和生命周期
在JavaScript中异步任务里的微任务和宏任务的特点和生命周期
370 0
|
JavaScript 安全 中间件
深入浅出Node.js中间件机制
【10月更文挑战第4天】在探索Node.js的海洋中,中间件机制犹如一座灯塔,为开发者指引方向。本文将带你一探究竟,从浅入深地理解这一核心概念。我们将通过生动的比喻和实际代码示例,揭示中间件如何在请求和响应之间搭建桥梁,实现功能的扩展与定制。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
350 0