事件循环
Wupq 2022-08-01 JavaScriptJS习题
# 事件循环
在 JavaScript 中,事件循环(Event Loop)是其异步编程模型的核心机制。
- 基本结构
- 调用栈(Call Stack):当函数被调用时,会被压入调用栈。当函数执行完毕后,会从调用栈中弹出。
- 任务队列(Task Queue):也称为宏任务队列(macrotask queue),存放着宏任务,如 setTimeout、setInterval、I/O 操作等。
- 微任务队列(Microtask Queue):存放着微任务,如 Promise 的回调函数、MutationObserver 等。
- 工作流程
- JavaScript 引擎首先执行同步代码,这些代码会依次被压入调用栈并执行。
- 当遇到异步任务时,JavaScript 引擎会将其交给浏览器的其他模块(如定时器模块、网络请求模块等)去处理,并继续执行后续的同步代码。
- 当异步任务完成后,会将对应的回调函数放入任务队列中。
- 当调用栈为空时,JavaScript 引擎会检查任务队列。如果任务队列中有宏任务,会取出一个宏任务并执行。
- 宏任务执行完毕后,会检查微任务队列。如果微任务队列中有任务,会依次执行微任务队列中的所有任务,直到微任务队列为空。
- 然后,继续从任务队列中取出下一个宏任务进行执行,如此循环往复。
- 示例
console.log('同步代码开始执行');
setTimeout(() => {
console.log('setTimeout 回调执行');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 微任务执行');
});
console.log('同步代码结束执行');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在这个例子中,首先会输出 “同步代码开始执行” 和 “同步代码结束执行”,然后执行微任务队列中的 “Promise 微任务执行”,最后执行宏任务队列中的 “setTimeout 回调执行”。
- 作用和意义
- 实现非阻塞编程:允许 JavaScript 在执行耗时的异步操作时,不会阻塞后续代码的执行,从而提高程序的响应性。
- 更好地处理用户交互:在处理用户界面的交互事件时,能够及时响应用户操作,提供流畅的用户体验。
- 优化性能:通过合理地安排异步任务的执行顺序,可以提高程序的性能和效率。
# 练习题1
# 输出是什么?
const first = () =>
new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
console.log(p);
}, 0);
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
});
first().then((arg) => {
console.log(arg);
});
console.log(4);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
答案
答案: 3,7,4,1,2,5,Promise{1}
//第一步
输出: 3, 7, 4
微任务 = [
p.then((arg) => {
console.log(arg);
})
first().then((arg) => {
console.log(arg);
})
]
宏任务 = [
() => {
console.log(5);
resolve(6);
console.log(p);
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//第二步
执行:
p.then((arg) => {
console.log(arg);
})
输出: 3, 7, 4, 1
微任务 = [
first().then((arg) => {
console.log(arg);
})
]
宏任务 = [
() => {
console.log(5);
resolve(6);
console.log(p);
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//第三步
执行:
first().then((arg) => {
console.log(arg);
})
输出: 3, 7, 4, 1, 2
微任务 = []
宏任务 = [
() => {
console.log(5);
resolve(6);
console.log(p);
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//第四步
执行:
() => {
console.log(5);
resolve(6);
console.log(p);
}
输出: 3, 7, 4, 1, 2, Promise
微任务 = []
宏任务 = []
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 练习题2
# 输出是什么?
let a;
let b = new Promise((resolve) => {
console.log(1);
setTimeout(() => {
resolve();
}, 1000);
}).then(() => {
console.log(2);
})
a = new Promise(async (resolve) => {
console.log(a);
await b;
console.log(a);
console.log(3);
await a;
resolve(true);
console.log(4);
})
console.log(5);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
答案
答案: 1, a(undefined)
, 5, "等待一秒", 2, Promise{<pendding>}
, 3
// 关于这种涉及 await 的题目,可以简单先做一下形转化,统一转换为 promise,这样便于理解
let a;
let b = new Promise((resolve) => {
console.log(1);
setTimeout(() => {
resolve();
}, 1000);
}).then(() => {
console.log(2);
})
a = new Promise(async (resolve) => {
console.log(a);
b.then(() => {
console.log(a);
console.log(3);
a.then(() => {
resolve(true);
console.log(4);
})
})
})
console.log(5);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 第一步
输出: 1, a(undefined), 5
微任务 = [
b.then(() => {
console.log(2);
})
.then(() => {
console.log(a);
console.log(3);
a.then(() => {
resolve(true);
console.log(4);
})
})
]
宏任务 = [
() => {
Promise {
b
}.resolve(), 1000
}
]
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
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
// 第二步
//因为b.then在setTimeout中resolve, 所以这里优先执行
(() => {
Promise {
b
}.resolve()
}, 1000);
输出: 1, a(undefined), 5, '等待一秒'
微任务 = [
(() => {
console.log(2);
})
.then(() => {
console.log(a);
console.log(3);
a.then(() => {
resolve(true);
console.log(4);
})
})
]
宏任务 = []
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
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
// 第三步
执行:
(() => {
console.log(2);
})
.then(() => {
console.log(a);
console.log(3);
a.then(() => {
resolve(true);
console.log(4);
})
})
输出: 1, a(undefined), 5, '等待一秒', 2
微任务 = [
() => {
console.log(a);
console.log(3);
a.then(() => {
resolve(true);
console.log(4);
})
}
]
宏任务 = []
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
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
// 第四步
执行:
() => {
console.log(a);
console.log(3);
a.then(() => {
resolve(true);
console.log(4);
})
}
输出: 1, a(undefined), 5, '等待一秒', 2, `Promise{<pendding>}`, 3
微任务 = [
a.then(() => {
resolve(true);
console.log(4);
})
]
宏任务 = []
//结束: 因为 a.then 需要被 resolve 才能被执行, 而 resolve 又在 a.then 内, 因此 a.then 无法执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23