setTimeout、Promise、Async/Await 异步机制 事件循环

javascript是一门单线程语言

将任务分为两类:

  • 同步任务
  • 异步任务

打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务。

同步的进入主线程,异步的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。
在这里插入图片描述
js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

举些例子:

1、setTimeout
1
2
3
4
5
console.log('script start')	//1. 打印 script start
setTimeout(function(){
console.log('settimeout') // 3. 打印 settimeout
})
console.log('script end') //2. 打印 script start

输出顺序:script start->script end->settimeout

2、Promise

Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。

1
2
3
4
5
6
7
8
9
console.log('script start')  //1. 打印 script start
let promise1 = new Promise(function (resolve) {
console.log('promise1') //2. 打印 promise1
resolve()
console.log('promise1 end') //3. 打印 promise1 end
}).then(function () {
console.log('promise2') //5. 打印 promise2
})
console.log('script end') //4. 打印 script end

// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout

3、async await
1
2
3
4
5
6
7
8
9
10
11
async function async1(){
console.log('async1 start'); //2. 打印 async1 start
await async2();
console.log('async1 end') //5. 打印 async1 end
}
async function async2(){
console.log('async2') //3. 打印 async2
}
console.log('script start'); //1. 打印 script start
async1();
console.log('script end') //4. 打印 script end

// 输出顺序:script start->async1 start->async2->script end->async1 end

同时有setTimeout、Promise、Async/Await ,执行顺序又是什么?引入

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
在这里插入图片描述

1
2
3
4
5
6
7
8
9
setTimeout(function() {
console.log('setTimeout'); //4. 打印 setTimeout
})
new Promise(function(resolve) {
console.log('promise'); //1. 打印 promise
}).then(function() {
console.log('then'); //3. 打印 then
})
console.log('console'); //2. 打印 console

// 输出顺序:promise->console->then->setTimeout

  • 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。
  • 接下来遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue。
  • 遇到console.log(),立即执行。
  • 整体代码script作为第一个宏任务执行结束,发现了then在微任务Event Queue里面,执行。
  • 第一轮事件循环结束了,从宏任务Event Queue开始第二轮循环。发现了宏任务EventQueue中setTimeout对应的回调函数,立即执行。
  • 结束。

再来分析两个案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
console.log('1');  //1. 打印 1

setTimeout(function() {
console.log('2'); //5. 打印 2
process.nextTick(function() {
console.log('3'); //7. 打印 3
})
new Promise(function(resolve) {
console.log('4'); //6. 打印 4
resolve();
}).then(function() {
console.log('5') //8. 打印 5
})
})

process.nextTick(function() {
console.log('6'); //3. 打印 6
})

new Promise(function(resolve) {
console.log('7'); //2. 打印 7
resolve();
}).then(function() {
console.log('8') //4. 打印 8
})

setTimeout(function() {
console.log('9'); //9. 打印 9
process.nextTick(function() {
console.log('10'); //11. 打印 10
})

new Promise(function(resolve) {
console.log('11'); //10. 打印 11
resolve();
}).then(function() {
console.log('12') //12. 打印 12
})
})

// 输出顺序:1,7,6,8,2,4,3,5,9,11,10,12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async function async1() {
console.log('async1 start') //2. 打印 async1 start
await async2()
console.log('async1 end') //7. 打印 async1 end
}
async function async2() {
console.log('async2') //3. 打印 async2
}

console.log('script start') //1. 打印 script start
setTimeout(function () {
console.log('settimeout') //8. 打印 settimeout
})
async1()
new Promise(function (resolve) {
console.log('promise1') //4. 打印 promise1
resolve()
}).then(function () {
console.log('promise2') //6. 打印 promise2
})
console.log('script end') //5. 打印 script end

// 输出顺序:script start->async1 start->async2->promise1->script end->promise2->async1 end->settimeout

0%