/* 先打印1, 3, 2s后打印2 */
console.log(1);
setTimeout(() => {
console.log(2);
}, 1000);
console.log(3);
/* 先打印1, 3, 后打印2 */
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);第一个例子的话不难理解,定时器函数就是 1s 后才调用回调函数.
而第二个例子则可能优点小问题,JavaScript 从上到下执行,那么遇到 0s 的计时器函数,就应该先输出 2 才对啊。这就是因为后面要提到的 JavaScript 执行机制导致的啦,因为 setTimeout 是异步任务。
JavaScript 的核心特征就是单线程,即同一时间只能做一件事。
为什么它是单线程呢?因为 JavaScript 作为浏览器脚本语言,它的主要用途就是与用户互动、操作 DOM。既然如此,如果它不是单线程的话,假如一个线程在 DOM 节点上添加内容,同时另一个线程删除这个节点。可以看出,如果 JavaScript 不是单线程的话,那么将会导致同步问题。
JavaScript 是单线程语言,这也就导致了如果有一个任务等待很长的时间,这个时候就会导致阻塞,程序就会“卡死”,用户体验非常差。所以 JavaScript 需要异步任务。
那么,为什么 JavsScript 明明是单线程的,为什么能异步呢?这是因为浏览器是多线程的,通过事件循环 Event Loop即可实现异步。
所有任务都可以分成两种。
常见异步任务:
Event Table注册回调函数 Event Table移入到任务队列 Event Queue中 Event Queue中看看有没有回调函数,有的话就推到主线程中
这就是Event Loop
异步任务又可以进行更精细的划分为宏任务和微任务。
setTimeout、setInterval、requestAnimationFrame
Promise 回调部分、process.nextTick
script下的同步任务
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
new Promise((resolve, reject) => {
for (let i = 3; i < 6; i++) {
console.log(i);
}
resolve();
}).then(() => {
console.log(6);
});
console.log(7);
打印顺序:1, 3, 4, 5, 7, 6, 2
解析:
setTimeout后,把它放到宏任务队列中[setTimeout](这里用数组表示任务队列,左边代表先进入的任务队列) Promise,因为Promise 定义部分为同步任务,依次输出 3, 4, 5,遇到 Promise.then(),把它放到微任务队列中[setTimeout][Promise.then()]题目是本人自己想的,分析有误请见谅(希望评论指示)
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
}, 10);
const p = new Promise((resolve, reject) => {
console.log(4);
resolve(5);
})
.then((value) => {
console.log(value);
Promise.resolve().then(() => {
console.log(6);
});
setTimeout(() => {
console.log(7);
});
})
.then(() => {
console.log(8);
});
console.log(9);
输出顺序:1, 4, 9, 5, 6, 8, 7, 2, 3
解析:
Event Table中 Event Table中有 [2(后面还有其他的内容)]。 Event Table中没有顺序,满足触发条件后,就会进入对应的任务队列 Event Table中有 [2(后面还有其他的内容)] Event Table中有 [2(后面还有其他的内容)] Event Table中有 [2(后面还有其他的内容)][6, 8],6 会在 8 之前,因为 6 是微任务队列 5 里的微任务async/await 本质上还是基于 Promise 的一些封装,而 Promise 是属于微任务的一种。所以在使用 await 关键字与 Promise.then 相同。 async 函数在 await 之前的代码都是同步执行的,await 之后的代码则是属于微任务(类似于 Promise)await 的表达式还是属于同步任务
下面就继续练手
console.log(1);
async function async1() {
console.log(2);
await async2();
console.log(3);
}
async1();
async function async2() {
console.log(4);
await new Promise((resolve, reject) => {
console.log(5);
resolve(6);
}).then((value) => {
console.log(value);
});
console.log(7);
}
console.log(8);输出顺序为:1, 2, 4, 5, 8, 6, 7, 3
分析:
async1函数,因为 await之前包括 await的表达式都是同步任务,所以,输出 2后,进入到 async2函数中await一个 Promise也是同理,输出 5,6 进入微任务队列,因为await 之后的代码则是属于微任务(不包括 await 的表达式),所以 7 进入微任务队列 async2函数后,回到 async1函数中,之后的 3 进入微任务队列参考链接: JavaScript 运行机制详解:再谈 Event Loop 彻底搞懂 JavaScript 执行机制 JavaScript 之彻底理解 EventLoop 10 分钟理解 JS 引擎的执行机制