#挑战30天在头条写日记#
Promise.resolve()
.then(() => {
console.log(0)
return Promise.resolve(4)
})
.then(res => {
console.log(res)
});
Promise.resolve()
.then(() => {
console.log(1)
})
.then(() => {
console.log(2)
})
.then(() => {
console.log(3)
})
.then(() => {
console.log(5)
})
.then(() => {
console.log(6)
})
上面代码的输出结果是什么?为什么会输出这样的结果呢?
核心知识点
JavaScript中的任务和微任务:
在JavaScript中,任务(Task)是指要在JavaScript引擎中执行的一个工作单元。任务可以是同步的(立即执行)或异步的(延迟执行或在后台执行)。任务被放入任务队列中,等待JavaScript引擎执行。
微任务(Microtask)和宏任务(Macrotask)是两种不同类型的任务,它们被添加到不同的任务队列中,且执行顺序也有所不同。
微任务(Microtask):
微任务是一类需要尽快执行的任务,它们执行在当前任务(当前执行栈)结束之后、下一个任务(事件循环迭代)开始之前。也就是说,当当前执行栈中的代码执行完毕,JavaScript引擎会立即检查微任务队列,并在执行栈为空时按顺序执行微任务队列中的任务。
常见产生微任务的方式是使用Promise、async/await、queueMicrotask(标准化的微任务API)等。
例如:
console.log('Start');
Promise.resolve().then(() => console.log('Microtask 1'));
Promise.resolve().then(() => console.log('Microtask 2'));
console.log('End');
输出结果:
Start
End
Microtask 1
Microtask 2
在javascript中创建微任务有哪些方式?
Promise:
Promise 是一种内置对象,它表示一个异步操作的最终完成或失败,并返回一个结果值。Promise的回调函数(.then()、.catch()、.finally())会被放入微任务队列,等待当前执行栈为空时执行。
const myPromise = new Promise((resolve, reject) => {
// 异步操作
if (/* 异步操作成功 */) {
resolve(result); // 将结果传递给.then()回调
} else {
reject(error); // 将错误传递给.catch()回调
}
});
myPromise.then(result => {
// 处理成功结果
}).catch(error => {
// 处理错误情况
}).finally(() => {
// 最终处理,不管成功还是失败都会执行
});
async/await:
async/await 是 Promise 的语法糖,它使得异步代码看起来更像同步代码。async 函数内部使用 await 来等待 Promise 完成,它们也会创建微任务。
async function myAsyncFunction() {
try {
const result = await someAsyncOperation(); // 等待异步操作完成
// 处理成功结果
} catch (error) {
// 处理错误情况
} finally {
// 最终处理
}
}
MutationObserver:
MutationObserver 是一个用于监测 DOM 变化的接口,它可以用于监听 DOM 的变化并在变化发生后执行回调函数,该回调函数会被放入微任务队列。
const targetNode = document.getElementById('target');
const observer = new MutationObserver(mutations => {
// 处理 DOM 变化
});
const config = { attributes: true, childList: true, subtree: true };
observer.observe(targetNode, config);
宏任务
宏任务是一类需要在当前任务之后、下一个事件循环迭代开始之前执行的任务。它们在任务队列中排队,只有当执行栈为空(所有微任务执行完毕)时,才会从宏任务队列中取出一个任务执行。宏任务可以通过setTimeout、setInterval、requestAnimationFrame、DOM事件回调(例如点击事件、AJAX回调)等来创建,这些任务会被放入宏任务队列。请注意,在每次事件循环中,微任务优先于宏任务执行。
console.log('Start');
setTimeout(() => console.log('Macrotask 1'), 0);
setTimeout(() => console.log('Macrotask 2'), 0);
console.log('End');
输出结果:
Start
End
Macrotask 1
Macrotask 2
任务队列按照先进先出(FIFO)的顺序执行任务,即先进入队列的任务先执行。在事件循环的每一次迭代中,JavaScript引擎会依次执行当前的微任务队列中的所有任务,然后执行当前的宏任务队列中的一个任务,然后进入下一次事件循环迭代。这样的过程不断重复,形成了事件循环的机制。
面试题分析
上面的图示里,可以看到整个宏任务执行的过程,Promise.then创建了微任务队列。到这一步,宏任务执行完毕,微任务队列也构建完毕,接下来开始执行微任务队列里的内容。此时微任务队列里的任务如下所示:
Promise.resolve()
.then(() => { // microtask 1
console.log(0) // 打印0
// 1.返回一个Promise对象
// 2.resolve 4,返回4,进入then
return Promise.resolve(4)
})
.then(res => { // microtask 1-1
console.log(res)
});
Promise.resolve()
.then(() => { // microtask 2
console.log(1) // 打印1,这时microtask1的1执行完成
})
.then(() => { // microtask 3
console.log(2) // 打印2,这时microtask1的2执行完成,resolve了4并进入then
})
.then(() => { // microtask 4
console.log(3) // 打印3,这时microtask 1-1执行,打印4
})
.then(() => { // microtask 5
console.log(5) // 继续执行,打印5
})
.then(() => { // microtask 6
console.log(6) // 继续执行,打印6
})
延伸扩展
setTimeout(() => {
console.log(`sto 0`)
}, 0);
Promise.resolve()
.then(() => {
setTimeout(() => {
console.log(`sto microtask1`)
}, 0);
console.log(0)
return Promise.resolve(4)
})
.then(res => {
setTimeout(() => {
console.log(`sto microtask1-1`)
}, 0);
console.log(res)
});
setTimeout(() => {
console.log(`sto 1`)
}, 0);
Promise.resolve()
.then(() => {
setTimeout(() => {
console.log(`sto microtask2`)
}, 0);
console.log(1)
})
.then(() => {
console.log(2)
})
.then(() => {
console.log(3)
})
.then(() => {
console.log(5)
})
.then(() => {
console.log(6)
});
setTimeout(() => {
console.log(`sto 2`)
}, 0);
上面的代码会怎么样输出呢?为什么呢?