云计算百科
云计算领域专业知识百科平台

深入理解 JavaScript 异步编程:从 Event Loop 到 Promise

个人空间:https://blog.csdn.net/m0_73589512https://blog.csdn.net/m0_73589512

大家好,我是叁佰万~~

如果本文对您有帮助,请献上你的小❥(^_-)哟~~~

作为前端开发者,我们每天都在和 JavaScript 的异步特性打交道 —— 定时器、网络请求、事件回调…… 但你真的理解 JS 单线程下的异步底层逻辑吗?本文将从进程线程基础讲起,一步步拆解 Event Loop、宏微任务、Promise 核心原理,帮你彻底搞懂 JS 异步编程。

接续上文:https://blog.csdn.net/m0_73589512/article/details/157133111https://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 完整执行流程
  • 执行调用栈中的同步代码(宏任务的一部分);
  • 执行完同步代码后,清空所有微任务队列(按顺序执行);
  • 如有需要,执行 UI 渲染;
  • 从宏任务队列中取出一个宏任务执行;
  • 重复步骤 2-4,形成「事件循环」。
  • 核心原则:先同步,再微任务,最后宏任务;每次执行完一个宏任务,都要清空微任务队列。

    三、异步解决方案: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函数:声明后返回值自动包装为 Promise;
  • await关键字:只能在 async 函数内使用,等待 Promise 完成并返回结果;
  • 错误处理:可通过try/catch捕获异步错误,更符合同步代码的写法。
  • 示例:

    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 的异步编程体系可以总结为:

  • 底层支撑:浏览器多进程 / 多线程为 JS 单线程提供异步能力;
  • 核心机制:Event Loop 通过宏任务 / 微任务队列实现异步调度;
  • 解决方案:Promise 解决回调地狱,async/await 让 Promise 写法更优雅。
  • 理解这些核心概念,不仅能帮你搞定面试中的异步高频题,更能让你在实际开发中精准定位异步相关的 bug(比如定时器误差、宏微任务执行顺序问题)。异步是 JS 的核心,也是前端进阶的必经之路,希望这篇文章能帮你建立清晰的知识体系。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 深入理解 JavaScript 异步编程:从 Event Loop 到 Promise
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!