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指定的是在下次"事件循环"触发,所以很显然,前者总是比后者发生得早,而且执行效率也高(因为不用检查"任务队列")。

目录
相关文章
|
9天前
|
自然语言处理 JavaScript 前端开发
JavaScript闭包是函数访问外部作用域变量的能力体现,它用于封装私有变量、持久化状态、避免全局污染和处理异步操作。
【6月更文挑战第25天】JavaScript闭包是函数访问外部作用域变量的能力体现,它用于封装私有变量、持久化状态、避免全局污染和处理异步操作。闭包基于作用域链和垃圾回收机制,允许函数记住其定义时的环境。例如,`createCounter`函数返回的内部函数能访问并更新`count`,每次调用`counter()`计数器递增,展示了闭包维持状态的特性。
24 5
|
7天前
|
前端开发 JavaScript
Promise是JavaScript解决异步问题的构造器,代表未来的不确定值。
【6月更文挑战第27天】Promise是JavaScript解决异步问题的构造器,代表未来的不确定值。它避免了回调地狱,通过链式调用`.then()`和`.catch()`使异步流程清晰。
11 2
|
14小时前
|
JavaScript API
前后端数据交互.js文件的axios的写法,想要往后端发送数据,页面注入API,await的意思是同步等待服务器数据,并返回,axios注入在其他页面,其他页面调用的时候,同步作用
前后端数据交互.js文件的axios的写法,想要往后端发送数据,页面注入API,await的意思是同步等待服务器数据,并返回,axios注入在其他页面,其他页面调用的时候,同步作用
|
12天前
|
XML 移动开发 前端开发
JS设置Ajax为同步或异步
JS设置Ajax为同步或异步
11 0
|
13天前
|
JavaScript 前端开发
js中的宏任务与微任务
js中的宏任务与微任务
|
2月前
|
前端开发 JavaScript UED
深入理解 JavaScript 同步和异步,让网页灵动起来!
深入理解 JavaScript 同步和异步,让网页灵动起来!
|
11月前
|
JavaScript 前端开发
JavaScript同步、异步及事件循环
JavaScript同步、异步及事件循环
81 1
|
2月前
|
JavaScript
JS中同步和异步的区别
JS中同步和异步的区别
35 0
|
11月前
|
JavaScript 前端开发
js 的同步与异步,如何设置
js 的同步与异步,如何设置
112 0
|
11月前
|
JavaScript 前端开发 UED
js的同步异步
js的同步异步