JavaScript 的单线程与异步机制
JavaScript 是单线程语言,意味着同一时间只能执行一个任务。异步机制通过事件循环(Event Loop)、任务队列和 Web APIs 协作实现,避免阻塞主线程。
事件循环(Event Loop)
事件循环是 JavaScript 异步的核心机制。它不断检查调用栈(Call Stack)和任务队列(Task Queue),当调用栈为空时,将队列中的任务推入调用栈执行。
- 调用栈:存储同步任务的执行上下文,后进先出(LIFO)。
- 任务队列:存储异步任务的回调函数,分为宏任务(MacroTask)和微任务(MicroTask)。
Web APIs 与异步任务
浏览器或 Node.js 提供的 Web APIs(如 setTimeout、XMLHttpRequest)在异步操作完成后,将回调函数放入任务队列。
- 宏任务 :包括
setTimeout、setInterval、I/O 操作等,由事件循环按顺序处理。 - 微任务 :包括
Promise.then、MutationObserver,在宏任务执行结束后立即执行,优先级高于宏任务。
示例流程
- 同步代码执行,遇到异步任务(如
setTimeout)时,交给 Web API 处理。 - Web API 在异步操作完成后,将回调函数放入任务队列。
- 调用栈为空时,事件循环从任务队列中取出回调函数执行。
代码示例
javascript
console.log("Start"); // 同步任务
setTimeout(() => {
console.log("Timeout"); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log("Promise"); // 微任务
});
console.log("End"); // 同步任务
输出顺序:
Start
End
Promise
Timeout
关键点
- 微任务(如
Promise)优先于宏任务(如setTimeout)执行。 - 事件循环确保异步任务不阻塞主线程,通过队列机制有序处理回调。
这种机制使得单线程的 JavaScript 能够高效处理并发操作。
Promise 的实现原理
Promise 是 JavaScript 中用于处理异步操作的一种机制,其核心原理基于状态机、回调函数队列和链式调用。以下是其实现原理的关键点:
状态机
Promise 有三种状态:pending(等待)、fulfilled(完成)和 rejected(拒绝)。状态一旦从 pending 转变为 fulfilled 或 rejected,就不可再改变。这种状态机设计确保了 Promise 的不可逆性。
构造函数
Promise 构造函数接收一个执行器函数(executor),该函数会立即执行。执行器函数包含两个参数:resolve 和 reject,用于改变 Promise 的状态并传递结果或错误。
javascript
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(cb => cb());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.value = reason;
this.onRejectedCallbacks.forEach(cb => cb());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
then 方法
then 方法用于注册回调函数,接收 onFulfilled 和 onRejected 两个参数。根据当前状态,立即执行回调或将回调存入队列(若状态为 pending)。then 返回一个新的 Promise,支持链式调用。
javascript
then(onFulfilled, onRejected) {
const newPromise = new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
try {
if (typeof onFulfilled === 'function') {
const result = onFulfilled(this.value);
resolve(result);
} else {
resolve(this.value);
}
} catch (error) {
reject(error);
}
};
const handleRejected = () => {
try {
if (typeof onRejected === 'function') {
const result = onRejected(this.value);
resolve(result);
} else {
reject(this.value);
}
} catch (error) {
reject(error);
}
};
if (this.state === 'fulfilled') {
setTimeout(handleFulfilled, 0);
} else if (this.state === 'rejected') {
setTimeout(handleRejected, 0);
} else {
this.onFulfilledCallbacks.push(() => setTimeout(handleFulfilled, 0));
this.onRejectedCallbacks.push(() => setTimeout(handleRejected, 0));
}
});
return newPromise;
}
异步调度
回调函数通过 setTimeout 或微任务(如 queueMicrotask)异步执行,确保 then 方法总是在当前执行栈结束后运行,符合 Promises/A+ 规范。
链式调用与值穿透
若 then 的参数不是函数,则直接将值传递给下一个 Promise。若回调返回一个 Promise,则会等待其状态改变后再继续链式调用。
javascript
const promise = new MyPromise((resolve) => resolve(1))
.then(val => val + 1)
.then(val => console.log(val)); // 输出 2
错误处理
通过 try/catch 捕获执行器或回调中的错误,并调用 reject 方法。catch 方法本质上是 then(null, onRejected) 的语法糖。
总结
Promise 的核心是通过状态管理、回调队列和链式调用实现异步操作的顺序控制。其设计确保了代码的可读性和可维护性,避免了回调地狱(Callback Hell)。