个人空间:https://blog.csdn.net/m0_73589512
https://blog.csdn.net/m0_73589512
大家好,我是叁佰万~~
如果本文对您有帮助,请献上你的小❥(^_-)哟~~~
作为前端开发者,我们每天都在和 JavaScript 的异步特性打交道 —— 定时器、网络请求、事件回调…… 但你真的理解 JS 单线程下的异步底层逻辑吗?本文将从进程线程基础讲起,一步步拆解 Event Loop、宏微任务、Promise 核心原理,帮你彻底搞懂 JS 异步编程。
接续上文:https://blog.csdn.net/m0_73589512/article/details/157133111
https://blog.csdn.net/m0_73589512/article/details/157133111
目录
一、基础认知:进程与线程
二、核心机制:Event Loop(事件循环)
2.1 同步与异步的本质
2.2 宏任务与微任务
Event Loop 完整执行流程
三、异步解决方案:Promise
3.1 Promise 核心特性
3.2 Promise 常用静态方法
3.3 手写 Promise(核心实现)
四、语法糖:async/await
核心特点
五、总结
一、基础认知:进程与线程
要理解 JS 异步,首先得搞清楚「进程」和「线程」这两个基础概念,这是浏览器实现异步的硬件底层支撑。
| 通俗类比 | 工厂里的独立车间 | 车间里的工人 |
| 核心定位 | CPU 资源分配的最小单位 | CPU 调度执行的最小单位 |
| 成本特性 | 创建 / 销毁成本高(重量级) | 创建 / 销毁成本低(轻量级) |
| 前端示例 | 浏览器每新开一个标签页 = 新进程 | 浏览器进程内包含多线程:GUI 渲染线程、JS 引擎线程、定时器线程、异步 HTTP 请求线程、事件触发线程 |
这里有个关键知识点:JavaScript 是单线程脚本语言,但浏览器是多线程、多进程的。JS 的单线程意味着它同一时间只能做一件事,而浏览器的多线程则为 JS 实现异步提供了基础。
二、核心机制:Event Loop(事件循环)
JS 单线程要处理同步和异步任务,全靠 Event Loop 这个核心机制。我们先拆解它的执行逻辑。
2.1 同步与异步的本质
- 同步代码:按顺序执行,会阻塞后续代码(比如普通的console.log、变量声明);
- 异步代码:不阻塞主线程,进入「任务队列」等待执行(比如setTimeout、网络请求)。
举个简单例子:
// 同步代码:顺序执行
console.log('1');
console.log('2');
// 异步代码:先执行同步,再执行异步
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
// 输出:1 → 3 → 2
2.2 宏任务与微任务
异步任务并非「一视同仁」,而是分为宏任务和微任务,执行优先级有明确区分:
- 宏任务:脚本代码本身、setTimeout/setInterval、I/O 操作、UI 渲染等;
- 微任务:Promise.then/catch/finally、queueMicrotask、Node.js 的process.nextTick等。
Event Loop 完整执行流程
核心原则:先同步,再微任务,最后宏任务;每次执行完一个宏任务,都要清空微任务队列。
三、异步解决方案:Promise
早期异步编程依赖嵌套回调函数,容易形成「回调地狱」(金字塔形代码、错误处理困难、复用性差),而 Promise 是解决回调地狱的标准方案。
3.1 Promise 核心特性
三种状态:
- pending:默认状态(等待中);
- fulfilled:成功状态(不可逆);
- rejected:失败状态(不可逆)。状态只能从pending转为fulfilled或rejected,一旦改变就无法修改。
链式调用:Promise 的then方法会返回新的 Promise,支持链式调用,替代嵌套回调:
// 模拟异步请求
function fetchUser(id) {
return new Promise(resolve => {
setTimeout(() => resolve({ id, name: `用户${id}` }), 1000);
});
}
function fetchPosts(userId) {
return new Promise(resolve => {
setTimeout(() => resolve([`帖子1-${userId}`, `帖子2-${userId}`]), 1000);
});
}
// 链式调用:替代嵌套
fetchUser(1)
.then(user => {
console.log('用户:', user);
return fetchPosts(user.id); // 返回新Promise
})
.then(posts => console.log('帖子:', posts))
.catch(error => console.error('错误:', error));
3.2 Promise 常用静态方法
Promise 提供了多个静态方法,满足不同的异步场景需求:
- Promise.all:所有 Promise 都成功才返回成功,一个失败则整体失败;
- Promise.race:多个 Promise 中「最快完成」的那个决定结果(无论成功 / 失败);
- Promise.allSettled:等待所有 Promise 完成(无论成功 / 失败),返回每个 Promise 的结果;
- Promise.any:只要有一个 Promise 成功就返回成功,全部失败才返回失败。
示例代码:
// Promise.all:批量请求
Promise.all([fetchUser(1), fetchUser(2)])
.then(users => console.log('所有用户:', users))
.catch(err => console.error('单个失败:', err));
// Promise.race:超时控制
Promise.race([
fetchUser(1),
new Promise((_, reject) => setTimeout(() => reject('请求超时'), 800))
])
.then(user => console.log('请求成功:', user))
.catch(err => console.error('请求失败:', err));
3.3 手写 Promise(核心实现)
理解 Promise 最好的方式是手写一个简易版,核心逻辑包括状态管理、回调队列、then 方法链式调用:
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class MyPromise {
constructor(executor) {
this.status = PENDING; // 默认状态
this.value = undefined; // 成功值
this.reason = undefined; // 失败原因
this.onResolvedCallbacks = []; // 成功回调队列
this.onRejectedCallbacks = []; // 失败回调队列
// 成功回调
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
};
// 失败回调
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject); // 立即执行执行器
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 处理默认回调
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e };
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
// 解析Promise返回值(核心逻辑)
resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
let called = false;
if (x instanceof MyPromise) {
x.then(y => this.resolvePromise(promise2, y, resolve, reject), reject);
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
}, e => {
if (called) return;
called = true;
reject(e);
});
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
catch(onRejected) {
return this.then(null, onRejected);
}
}
四、语法糖:async/await
async/await是 Promise 的语法糖,它让异步代码看起来像同步代码,底层依然基于 Promise 实现。
核心特点
示例:
async function fetchData() {
try {
const user = await fetchUser(1);
console.log('用户:', user);
const posts = await fetchPosts(user.id);
console.log('帖子:', posts);
return posts;
} catch (error) {
console.error('请求失败:', error);
}
}
fetchData();
五、总结
JavaScript 的异步编程体系可以总结为:
理解这些核心概念,不仅能帮你搞定面试中的异步高频题,更能让你在实际开发中精准定位异步相关的 bug(比如定时器误差、宏微任务执行顺序问题)。异步是 JS 的核心,也是前端进阶的必经之路,希望这篇文章能帮你建立清晰的知识体系。
网硕互联帮助中心



评论前必须登录!
注册