在 javascript 事件循环中,`settimeout` 的回调属于宏任务(macrotask),而 `promise` 的 `.then` 回调属于微任务(microtask)。执行顺序由当前调用栈清空后,事件循环检查任务队列的优先级决定:先处理完所有待执行的微任务,再取出一个宏任务执行。因此,若两者在同一轮事件循环中被注册,`promise` 的回调会先于 `settimeout` 的回调执行。

同步代码与异步任务的划分依据是什么
JavaScript 引擎将任务划分为同步任务和异步任务。同步代码直接在主线程上按顺序执行,而异步任务则被放入不同的任务队列中等待调度。这种划分决定了代码的执行流:只有当调用栈为空时,事件循环才会从队列中取出任务压入调用栈。理解这一机制是掌握异步执行顺序的基础。

console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
上述代码的输出顺序为:Start、End、Promise、Timeout。Start 和 End 是同步代码,立即执行。setTimeout 被放入宏任务队列,Promise 的 .then 被放入微任务队列。同步代码执行完毕后,事件循环优先处理微任务队列中的 Promise 回调,最后才处理宏任务队列中的 setTimeout 回调。
嵌套异步操作如何影响执行顺序
当异步操作嵌套发生时,微任务可能会产生新的微任务,这些新产生的微任务会被追加到当前微任务队列的末尾,并在下一个宏任务执行前继续处理。这意味着,如果在一个 Promise 的回调中又创建了一个新的 Promise,新的 Promise 的回调会在当前所有其他微任务完成后才执行,但仍早于任何宏任务。
console.log('1');
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => {
console.log('3');
});
}, 0);
Promise.resolve().then(() => {
console.log('4');
setTimeout(() => {
console.log('5');
}, 0);
});
console.log('6');
该代码的输出顺序为:1、6、4、2、3、5。首先执行同步代码 1 和 6。然后处理微任务,输出 4,并在其中注册一个宏任务 5。接着处理剩余的微任务(无),然后处理宏任务,输出 2,并在其中注册一个微任务 3。由于 3 是在宏任务执行期间注册的,它会被放入微任务队列,在当前宏任务结束后立即执行,输出 3。最后处理之前注册的宏任务 5,输出 5。
Promise 链式调用中的错误处理时机
在 Promise 链式调用中,如果某个 .then 或 .catch 中抛出错误,或者返回一个拒绝状态的 Promise,后续的 .catch 会捕获这个错误。这个错误处理过程也是作为微任务执行的。如果错误未被捕获,它将导致未处理的 Promise 拒绝警告。理解这一点对于调试异步代码至关重要。
Promise.resolve()
.then(() => {
console.log('A');
throw new Error('Error in A');
})
.then(() => {
console.log('B');
})
.catch((err) => {
console.log('C', err.message);
});
setTimeout(() => {
console.log('D');
}, 0);
该代码的输出顺序为:A、C Error in A、D。首先,Promise.resolve() 立即解决,触发第一个 .then,输出 A 并抛出错误。这个错误被后续的 .catch 捕获,输出 C 和错误信息。由于 .catch 成功处理了错误,链式调用恢复正常,没有更多的微任务需要执行。最后,宏任务 setTimeout 执行,输出 D。注意,第二个 .then 不会被执行,因为错误已经被 .catch 捕获。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











