ajax宏任务,JS异步详解 - 浏览器/Node/事件循环/消息队列/宏任务/微任务

js异步历史

一个 JavaScript 引擎会常驻于内存中,它等待着我们把JavaScript 代码或者函数传递给它执行

在 ES3 和更早的版本中,JavaScript 本身还没有异步执行代码的能力,引擎就把代码直接顺次执行了,异步任务都是宿主环境(浏览器)发起的(setTimeout、AJAX等)。

在 ES5 之后,JavaScript 引入了 Promise,这样,不需要浏览器的安排,JavaScript 引擎本身也可以发起任务了

JS异步实现原理

js为单线程,js引擎中负责解析执行js代码的线程只有一个( 主线程 ),即每次只能做一件事,其他IO操作放入任务队列等待执行,异步过程中, 工作线程 在 异步操作完成后 需要 通知主线程 。那么这个 通知机制 是利用 消息队列 和 事件循环(EventLoop) 实际上, 主线程只会做一件事情 ,就是从消息队列里面取消息、执行消息,再取消息、再执行。当消息队列为空时,就会等待直到消息队列变成非空。而且 主线程只有在将当前的消息执行完成后,才会去取下一个消息

浏览器

概念

消息队列 :消息队列是一个 先进先出 的队列,它里面存放着各种消息。

事件循环 :事件循环是指 主线程重复从消息队列中取消息、执行的过程。(浏览器至少有一个事件循环,一个事件循环至少有一个任务队列(macrotask))

微任务:

JavaScript 引擎发起的任务 - JS 引擎级别

promise回调,MutationObserver,process.nextTick,Object.observe

宏任务

宿主发起的任务,每次的一段js代码执行过程,其实都是一个宏观任务 - 宿主级别

整体的js代码,事件回调,XHR回调,定时器(setTimeout/setInterval/setImmediate),IO操作,UI render

宏任务和微任务关系:每个macro宏任务会维护一个micro微任务列表

事件循环过程

首先我们分析有多少个宏任务;

在每个宏任务中,分析有多少个微任务;

根据调用次序,确定宏任务中的微任务执行次序;

根据宏任务的触发规则和调用次序,确定宏任务的执行次序;

确定整个顺序

b38e3bf623115c4535ff9d04d0ee29c7.png

视图渲染时机:

本轮事件循环的microtask队列被执行完之后(不是每轮事件循环都会执行视图更新,浏览器有自己的优化策略)

注意:执行任务的耗时会影响视图渲染的时机。通常浏览器以每秒60帧(60fps)的速率刷新页面(16.7ms渲染一帧)所以如果要让用户觉得顺畅,单个macrotask及它相关的所有microtask最好能在16.7ms内完成。

3ac38ec955e775a155107a0d07ed4859.png

Node

概念

非阻塞 I/O 操作:尽管 JavaScript 是单线程处理的——当有可能的时候,它们会把操作转移到系统内核中去,当其中的一个操作完成的时候,内核通知 Node.js 将适合的回调函数添加到 轮询 队列中等待时机执行

事件循环过程

过程

event loop 的每个阶段都有一个任务队列(一个 FIFO 队列来执行回调)

当 event loop 到达某个阶段时,将执行该阶段的任务队列,直到队列清空或执行的回调达到系统上限后,才会转入下一个阶段

当所有阶段被顺序执行一次后,称 event loop 完成了一个 tick

每次事件循环都包含了6个阶段

timers 阶段 :这个阶段执行timer( setTimeout 、 setInterval )的回调

I/O callbacks 阶段 :执行一些系统调用错误,比如网络通信的错误回调

idle, prepare 阶段 :仅node内部使用

poll 阶段 :获取新的I/O事件, 适当的条件下node将阻塞在这里

check 阶段 :执行 setImmediate() 的回调

close callbacks 阶段 :执行 socket 的 close 事件回调

72e06fec2b6d20512654921b5783dd75.png

timers 阶段

Node 会去检查有无已过期的timer,如果有则把它的回调压入timer的任务队列中等待执行

技术上来说,poll 阶段控制 timers 什么时候执行。

poll 阶段

poll 阶段主要有2个功能:

处理 poll 队列的事件

当有已超时的 timer,执行它的回调函数

执行过程:当event loop进入 poll 阶段,并且 没有设定的timers(there are no timers scheduled),会发生下面两件事之一:

如果 poll 队列不空,event loop会遍历队列并同步执行回调,直到队列清空或执行的回调数到达系统上限;

如果 poll 队列为空,则发生以下两件事之一:

如果代码已经被setImmediate()设定了回调, event loop将结束 poll 阶段进入 check 阶段来执行 check 队列(里的回调)。

如果代码没有被setImmediate()设定回调,event loop将阻塞在该阶段等待回调被加入 poll 队列,并立即执行。

当event loop进入 poll 阶段,并且 有设定的timers,一旦 poll 队列为空(poll 阶段空闲状态): event loop将检查timers,如果有1个或多个timers的下限时间已经到达,event loop将绕回 timers 阶段,并执行 timer 队列。

注意:没有 setImmediate() 会导致event loop阻塞在poll阶段,这样之前设置的timer岂不是执行不了了?所以咧,在poll阶段event loop会有一个检查机制,检查timer队列是否为空,如果timer队列非空,event loop就开始下一轮事件循环,即重新进入到timer阶段。

process.nextTick() VS setImmediate()

process.nextTick() process.nextTick()

setImmediate

对比

setTimeout(()=>{

console.log('timer1')

Promise.resolve().then(function() {

console.log('promise1')

})

}, 0)

setTimeout(()=>{

console.log('timer2')

Promise.resolve().then(function() {

console.log('promise2')

})

}, 0)

//浏览器:

timer1

promise1

timer2

promise2

// node

timer1

timer2

promise1

promise2

7859b0dab7f59b4cb8906afa363dbe6a.png

1a6540a5219b9d7cb7df01b5649e93f6.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值