js 事件循环机制

事件循环机制

首先,JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环。

JavaScript中,所有的任务都可以分为:

  • 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
  • 异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等

根据上图解读:

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数 
  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。 
  • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。 
  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。 

宏任务和微任务

console.log('script start');

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

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

console.log('script end');

按照上述 图来分析这段代码,可以得到执行步骤:

  1. console.log('script start');  同步任务,主线程中执行, script start'
  2. setTimeout() 异步任务,放到eventTable, 在0 秒后 将回调 推入到 event queue(异步队列)中
  3. .then 异步任务 放到 eventTable, 回调函数 入 异步队列
  4. .then 异步任务 放到 eventTable, 回调函数 入 异步队列
  5. console.log('script end');  同步任务,主线程中执行, script end'

==> 输出结果应该是 script start  script end  setTimeout  promise1 promise2

==> 但是在浏览器 里 输出的是  script start   script end  promise1 promise2  setTimeout

宏任务(macro-task)

macro-task大概包括:

  • script(整体代码)
  • setTimeout
  • setInterval
  • setImmediate / I/O (node.js)
  • UI rendering (UI 渲染)

微任务(micro-task)

micro-task大概包括:

  • process.nextTick(Node.js) 
  • Promise.then 
  • MutationObserver (html5新特性) 

宏任务,微任务的关系如图所示

按照这个流程,它的执行机制是:

  1. 存在微任务的话,那么就执行所有的微任务
  2. 微任务都执行完之后,执行第一个宏任务,
  3. 循环 1, 2
console.log('script start');

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

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

console.log('script end');

// script start  script end promise1  promise2 setTimeout

流程如下:

  • console.log('script start');  同步任务,主线程中执行, script start'
  • 定时器,宏任务队列 后面执行
  • .then 微任务,微任务队列,后面执行
  • console.log('script end');  script end
  • 到 script end 本轮 同步代码 执行结束, 执行本次宏任务里所有微任务两个.then
  • 微结束之后 再 开启下一个 宏任务
<script>
	console.log('start1')
	setTimeout(() => console.log('timer1'), 0)
	new Promise((resolve, reject) => {
		console.log('p1')
		resolve()
	}).then(() => {
		console.log('then1')
	})
	console.log('end1')
</script>

<!-- 脚本 2 -->
<script>
	console.log('start2')
	setTimeout(() => console.log('timer2'), 0)
	new Promise((resolve, reject) => {
		console.log('p2')
		resolve()
	}).then(() => {
		console.log('then2')
	})
	console.log('end2')
</script>
  • 两个script 标签 两个宏任务  宏任务队列 里
  • 现在微任务是空的 可以理解成上一个执行结束了,开启一个 新宏任务
  • console.log('start1')  同步 主线程   打出start1
  • setTimeout 异步  宏任务队列 里===> 现在宏任务队列已经有两个宏任务了 再脚本2 之后
  • new Promise() 同步  打出p1
  • .then 是微任务  放 微任务队列
  • console.log('end1')  同步 主线程   打出end1

start1  p1 end1 then1

思考一下: 下一个宏任务是谁??? 脚本2

  • console.log('start2')  同步 主线程   打出start2
  • setTimeout 异步  宏任务队列 里
  • new Promise() 同步  打出p2
  • .then 是微任务  放 微任务队列
  • console.log('end2') 同步 主线程   打出end2

start2 p2 end2 then2  timer1 timer2

async与await

async

async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行

async函数返回一个 Promise 对象。

async function f() {
  return 'hello world';
}

function f() {
    return Promise.resolve('hello world');
}

await 命令

正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

async function f(){
    // 等同于
    // return 123
    return await 123
}
f().then(v => console.log(v)) // 12

不管await后面跟着的是什么,await都会阻塞后面的代码

async function fn1 (){
    console.log(1)
    await fn2()
    console.log(2) // 阻塞
}

async function fn2 (){
    console.log('fn2')
}

fn1()
console.log(3)

上面的例子中,await 会阻塞下面的代码(即加入微任务队列),先执行 async外面的同步代码,同步代码执行完,再回到 async 函数中,再执行之前阻塞的代码

所以上述输出结果为:1fn232

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blue_路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值