共计 4057 个字符,预计需要花费 11 分钟才能阅读完成。
1. 前言
这篇文章是想跟大家一起讨论一下 javascript
中高大上的Event Loop 事件循环机制
。
代码得仔细分析,能力绕的过去,否则容易绕晕,审慎再审慎~~~
事件循环次要讲的是异步执行问题,没方法,同步就是程序执行,没什么好说的。
<font color=gray> 至于什么线程、同步异步、调用栈、浏览器线程之类的概念,本文一律没有,有意者可自行查阅。</font>
2. 简介
2.1 工作队列
- 宏工作队列:script 整体代码、setTimeout/setInterval……
- 微工作队列:Promise
工作队列 在事件循环机制中施展着核心作用。是咱们理解事件循环机制的外围因素。
2.2 执行程序
1. 无 async/await 的执行程序
留神点前置:
-
Promise 中的函数属于同步函数
-
new Promise(function a(resolve, reject) {console.log('此为同步函数') })
-
- setTimeout、setInterval、Promise.then 等函数,不为实现状态的状况下,不会推送到工作队列中
图解:
流程图执行阐明:
- 首先执行同步代码
- 执行完结后查看微工作队列是否有工作,有则执行
- 微工作队列清空,查看宏工作队列是否有工作,有则执行
- 循环 2 和 3。直至两个队列都清空
- 执行完结
栗子:
console.log('1');
setTimeout(function() {console.log('2');
}, 0)
new Promise(function(resolve) {console.log('3');
resolve();}).then(function() {console.log('4');
});
console.log('5')
剖析:
- 首先执行同步代码,上述代码中
console.log(1)、console.log(3)
和console.log(5)
都属于同步代码。优先输入。同时执行 延时函数、then 函数 ,then 函数立刻返回后果,推送到 微工作队列 中 - 查看微工作队列,有
console.log(4)
工作,执行并输入。微工作队列完结。 - 延时函数倒计时实现推入宏工作队列。
- 查看宏工作队列,有
console.log(2)
工作,执行并输入。宏工作队列完结。 - 代码执行完结。
- 输入程序:
1,3,5,4,2
2.async 和 await 后的工夫循环机制
<font color=red> 揭示: 局部文章会介绍 await 让出执行权的问题,这里先不探讨,容易绕晕。有趣味的能够查看以下。</font>
await 代码的执行:
-
async 标记的函数属于同步函数
-
async function log () {console.log('同步函数') }
-
-
await 函数中返回一个 promise,则执行后为
pending
状态,将下方的代码作为微工作解决,并且,只有 await 函数执行之后才会执行下方代码(请看栗子 4)-
async function async2() {return new Promise(function(resolve) {console.log('3'); // 3 resolve();}).then(function() {console.log('4'); // 6 }); } console.log(async2()) // Promise {<pending>}
-
-
await 函数中执行一个 promise 但不 return 或者 resolve 被 setTimeout/setInterval 包裹,则执行后为 完结 状态,将下方的代码作为同步代码执行
-
// await 函数中执行一个 promise 但不 return async function async2() { // 区别在于没有 return new Promise(function(resolve) {console.log('3'); // 3 resolve();}).then(function() {console.log('4'); // 6 }); } console.log(async2()) // Promise {undefined} // resolve 被 setTimeout/setInterval 包裹 async function async2() {new Promise(function(resolve) {console.log('3'); setTimeout(() => {resolve(); }, 0) }).then(function() {console.log('4'); }); } console.log(async2()) // Promise {undefined}
-
栗子 1:
async function async1() {console.log('1');
await async2();
console.log('2');
}
async function async2() {return new Promise(function(resolve) {console.log('3');
resolve();}).then(function() {console.log('4');
});
}
console.log('5');
setTimeout(function() {console.log('6');
}, 0)
async1();
new Promise(function(resolve) {console.log('7');
resolve();}).then(function() {console.log('8');
});
console.log('9');
解析:
- 先执行同步,
console.log(5)、console.log(1)、console.log(3)、console.log(7)、console.log(9)
,将console.log(4)、console.log(8)
放入微工作队列。将console.log(6)
放入宏工作队列。 - 执行
console.log(4)
因为async2
返回的是promise
所以将前面的代码放入微工作队列中。此时微工作队列有两个工作console.log(8)、console.log(2)
。清空微工作队列,输入8 和 2
。 - 执行宏工作队列。输入
6
- 输入程序
5,1,3,7,9,4,8,2,6
栗子 2(栗子 1 的变种):
// 将 async2 改为上面这种写法,其余代码不变
async function async2() {new Promise(function(resolve) {console.log('3');
resolve();}).then(function() {console.log('4');
});
}
解析:
- 栗子 1 的 第一步 放弃不变
- 扭转第二步,执行
console.log(4)
因为async2
返回的不是promise
所以间接执行前面的代码。输入2
此时微工作队列只有console.log(8)
清空微工作队列,输入8
。 - 栗子 1 的 第三步 放弃不变
- 输入程序:
5,1,3,7,9,4,2,8,6
栗子 3(仍旧是栗子 1 的变种,最初一个小栗子):
async function async1() {console.log('1');
await async2();
console.log('2');
}
// 将 async2 中的 resolve 函数应用 setTimeout 包裹 且 return 一个 Promise
async function async2() {return new Promise(function(resolve) {console.log('3');
setTimeout(() => {resolve();
}, 0)
}).then(function() {console.log('4');
});
}
console.log('5');
setTimeout(function() {console.log('6');
}, 0)
async1();
new Promise(function(resolve) {console.log('7');
resolve();}).then(function() {console.log('8');
});
解析:
- 先执行同步,
console.log(5)、console.log(1)、console.log(3)、console.log(7)、console.log(9)
,将console.log(8)
放入微工作队列。将console.log(6) 和 console.log(4)
放入宏工作队列。 - 执行微工作
console.log(8)
,输入8
- 执行宏工作
console.log(6)
,输入6
- 因为
async2
返回一个Promise
,所以只能等到async2
执行之后才会将console.log(2)
推入到微工作队列。 - 执行宏工作
console.log(4)
,输入4
并将console.log(2)
推入到微工作中。 - 执行微工作
console.log(2)
,输入2
- 输入程序:
5,1,3,7,9,8,6,4,2
栗子 4(真的是最初一个小栗子了):
// 将栗子 3 的 async2 函数改为如下写法
async function async2() {new Promise(function(resolve) {console.log('3');
setTimeout(() => {resolve();
}, 0)
}).then(function() {console.log('4');
});
}
解析:
- 栗子 3 第一步不变
- 因为
async2
不返回Promise
,所以会将console.log(2)
作为同步代码执行,第二步执行同步工作console.log(2)
输入2
- 执行微工作
console.log(8)
,输入8
- 执行宏工作
console.log(6)
,输入6
- 执行宏工作
console.log(4)
,输入4
- 输入程序:
5,1,3,7,9,2,8,6,4
无论你是否认真的查看了下面的文章,首先祝贺你能够看到这里,事件队列始终是比拟难以了解的 javascript
知识点。还是衷心的心愿这篇文章能带给你不一样的了解。也祝愿正在看文章的你技术越来越好。