JS 是单线程语言。执行过程中产生两种任务:同步任务和异步任务。
- 同步任务:比如声明语句、for、赋值等,读取后依据从上到下从左到右,立即执行。
- 异步任务:比如 ajax 网络请求,setTimeout 定时函数等都属于异步任务。异步任务会通过任务队列(Event Queue)的机制(先进先出的机制)来进行协调。
任务队列
任务队列中的任务也分为两种,分别是:宏任务(Macro-take)和微任务(Micro-take)
- 宏任务主要包括:scrip(JS 整体代码)、setTimeout、setInterval、setImmediate、I/O、UI 交互
- 微任务主要包括:Promise(重点关注:值的是 then 部分)、process.nextTick(Node.js)、MutaionObserver
执行规则:理解这个就可以应对执行顺序啦
第一次初始化,先执行全局的 js 代码(scrip)的宏任务,然后进入 Event loop,此时先执行微任务,再执行宏任务。微/宏任务之间安装先进先出的机制执行。
例 1:
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 全局的宏任务(JS代码块)
//script end 全局的宏任务
//promise1 微任务
//promise2 微任务
//setTimeout 宏任务
例 2:
Promise 中的代码是同步代码,会立即执行的。只有 then 之后的代码,才是异步执行的代码,是一个微任务。
console.log("script start");
setTimeout(function () {
console.log("timeout1");
}, 10);
new Promise((resolve) => {
console.log("promise1");
resolve();
setTimeout(() => console.log("timeout2"), 10);
}).then(function () {
console.log("then1");
});
console.log("script end");
// script start JS代码块
// promise1 JS代码块
// script end JS代码块
// then1 微任务
// timeout1 宏任务
// timeout2 宏任务
例 3:
async/await 的执行,其实它就是 Promise 的语法糖
翻译过来:
// async/await 写法
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end"); // 这就是then里面的部分
}
// Promise 写法
async function async1() {
console.log("async1 start");
Promise.resolve(async2()).then(() => console.log("async1 end"));
}
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
setTimeout(() => {
console.log("timeout");
}, 0);
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log("script end");
// async1 start JS代码块
// async2 JS代码块 Promise里面的是同步代码,then里面的才是异步
// promise1 同上...
//script end 同上...
// primise2 微任务
// timeout 宏任务
例 4:
setTimeout 不太准确的,setTimeout 的第二个参数就是为了告诉 JS 再过多长的事件把当前的任务加到队列中。
如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行。
const s = new Date().getSeconds();
console.log("script start");
new Promise((resolve) => {
console.log("promise");
resolve();
}).then(() => {
console.log("then1");
while (true) {
if (new Date().getSeconds() - s >= 4) {
console.log("while");
break;
}
}
});
setTimeout(() => {
console.log("timeout");
}, 2000);
console.log("script end");
//script start JS代码块
//promise JS代码块
//script end JS代码块
//then1 微任务
4秒之后
//while
//timeout
// 这里虽然setTimeout 2s之后就添加到队列里面了,但是它前面有个微任务,所以要先等微任务执行完才行。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ZengXPang's blog!
评论