从语法糖到异步编程底层全解析

发布于:2025-04-13 ⋅ 阅读:(35) ⋅ 点赞:(0)

1. 语法糖的概念

定义

语法糖(Syntactic Sugar)是指在编程语言中添加的语法,它不会增加语言的表达能力,但使代码更易读、更简洁

示例

// 普通函数写法
function add(a, b) {
  return a + b;
}

// 箭头函数语法糖
const add = (a, b) => a + b;

2. 回调函数与回调地狱

回调函数概念

回调函数是作为参数传递给另一个函数,并在特定事件发生后执行的函数。

回调地狱示例

getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      getMoreData(c, function(d) {
        // 嵌套层级过深,代码难以维护
        console.log(d);
      });
    });
  });
});

回调地狱问题

  1. 代码可读性差
  2. 错误处理困难
  3. 调试复杂
  4. 代码维护成本高

3. Promise 基础

概念

Promise 是异步编程的一种解决方案,代表一个异步操作的最终完成(或失败)及其结果值。

状态

Promise 有三种状态:

  • Pending: 初始状态,既不是成功也不是失败
  • Fulfilled: 操作成功完成
  • Rejected: 操作失败

基本用法

const promise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 操作成功 */) {
    resolve(value); // 成功
  } else {
    reject(error); // 失败
  }
});

promise
  .then(value => {
    // 处理成功
  })
  .catch(error => {
    // 处理错误
  })
  .finally(() => {
    // 无论成功失败都会执行
  });

4. Promise 底层原理

Promise 实现原理

Promise 本质上是一个状态机,通过发布-订阅模式来实现异步操作的管理。

简化版 Promise 实现

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    const reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    // 简化实现
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    }
    if (this.state === 'rejected') {
      onRejected(this.reason);
    }
    if (this.state === 'pending') {
      this.onFulfilledCallbacks.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}

5. async/await 语法

概念

async/await 是 ES2017 引入的语法糖,用于更简洁地处理 Promise。

关键点

  • async 函数总是返回一个 Promise
  • await 只能在 async 函数内部使用
  • await 使 JavaScript 引擎等待 Promise 解决,然后继续执行

代码对比

// 使用 Promise 链
function getData() {
  return fetchData()
    .then(data => {
      return processData(data);
    })
    .then(result => {
      return formatData(result);
    })
    .catch(err => {
      console.error(err);
    });
}

// 使用 async/await
async function getData() {
  try {
    const data = await fetchData();
    const result = await processData(data);
    return await formatData(result);
  } catch (err) {
    console.error(err);
  }
}

6. async/await 底层原理

生成器函数

async/await 底层是基于生成器(Generator)和 Promise 实现的。

function* generatorFunction() {
  const data = yield fetchData();
  const result = yield processData(data);
  return formatData(result);
}

转换过程

JavaScript 引擎会将 async/await 转换为基于 Promise 的状态机和自动执行器。

// 简化版自动执行器
function run(generatorFunction) {
  const generator = generatorFunction();
  
  function handle(result) {
    if (result.done) return Promise.resolve(result.value);
    
    return Promise.resolve(result.value).then(
      res => handle(generator.next(res)),
      err => handle(generator.throw(err))
    );
  }
  
  return handle(generator.next());
}

7. Promise 常用方法

Promise.all()

并行执行多个 Promise,全部成功才成功,有一个失败则整体失败。

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then(values => {
    console.log(values); // [1, 2, 3]
  })
  .catch(error => {
    console.error(error);
  });

Promise.race()

返回最先解决或拒绝的 Promise 结果。

const promise1 = new Promise(resolve => setTimeout(() => resolve('一'), 500));
const promise2 = new Promise(resolve => setTimeout(() => resolve('二'), 100));

Promise.race([promise1, promise2])
  .then(value => {
    console.log(value); // '二' (更快)
  });

Promise.allSettled()

等待所有 Promise 完成(无论成功或失败)。

const promises = [
  Promise.resolve(1),
  Promise.reject('错误'),
  Promise.resolve(3)
];

Promise.allSettled(promises)
  .then(results => {
    console.log(results);
    // [
    //   { status: 'fulfilled', value: 1 },
    //   { status: 'rejected', reason: '错误' },
    //   { status: 'fulfilled', value: 3 }
    // ]
  });

Promise.any()

返回第一个成功的 Promise,全部失败才失败。

const promises = [
  Promise.reject('错误1'),
  Promise.resolve('成功'),
  Promise.reject('错误2')
];

Promise.any(promises)
  .then(value => {
    console.log(value); // '成功'
  })
  .catch(errors => {
    console.error(errors); // 所有 Promise 都拒绝时执行
  });

8. 实际应用场景

并行数据加载

async function loadDashboard() {
  try {
    // 并行加载数据
    const [userData, statsData, notificationsData] = await Promise.all([
      fetchUserData(),
      fetchStats(),
      fetchNotifications()
    ]);
    
    renderDashboard(userData, statsData, notificationsData);
  } catch (error) {
    showErrorMessage(error);
  }
}

超时处理

function fetchWithTimeout(url, ms) {
  const fetchPromise = fetch(url);
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('请求超时')), ms);
  });
  
  return Promise.race([fetchPromise, timeoutPromise]);
}

// 使用
fetchWithTimeout('/api/data', 5000)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

顺序处理与并行处理对比

// 顺序执行 - 总耗时是所有任务耗时之和
async function sequential() {
  console.time('sequential');
  
  const result1 = await task1();
  const result2 = await task2(result1);
  const result3 = await task3(result2);
  
  console.timeEnd('sequential');
  return result3;
}

// 并行执行 - 总耗时约等于最长任务的耗时
async function parallel() {
  console.time('parallel');
  
  const [result1, result2, result3] = await Promise.all([
    task1(), task2(), task3()
  ]);
  
  console.timeEnd('parallel');
  return [result1, result2, result3];
}

9. 异步编程最佳实践

错误处理

async function robustFunction() {
  try {
    const data = await riskyOperation();
    return processData(data);
  } catch (error) {
    // 处理特定错误
    if (error instanceof NetworkError) {
      return fallbackNetworkOperation();
    }
    // 记录并重新抛出其他错误
    logger.error(error);
    throw error;
  } finally {
    // 清理资源
    cleanup();
  }
}

避免回调地狱的方法

  1. 使用 Promise 链
  2. 采用 async/await
  3. 提取函数,减少嵌套
  4. 使用 Promise 组合方法
// 反模式: 回调地狱
function processData(callback) {
  fetchData(function(data) {
    processStep1(data, function(result1) {
      processStep2(result1, function(result2) {
        processStep3(result2, function(result3) {
          callback(result3);
        });
      });
    });
  });
}

// 最佳实践: async/await
async function processData() {
  try {
    const data = await fetchData();
    const result1 = await processStep1(data);
    const result2 = await processStep2(result1);
    return await processStep3(result2);
  } catch (error) {
    console.error('处理数据时出错:', error);
    throw error;
  }
}

10. 总结

概念图谱

语法糖 → 简化代码表达
  ↓
回调函数 → 回调地狱
  ↓
Promise → 优雅处理异步
  ↓
async/await → Promise的语法糖
  ↓
Promise.all/race/allSettled/any → 组合异步操作
  ↓
现代异步编程范式

各概念核心关系

  • 回调函数是最早的异步处理方式,但易导致回调地狱
  • Promise通过链式调用解决回调地狱问题
  • async/await是基于Promise的语法糖,使异步代码看起来像同步代码
  • Promise组合方法用于处理多个异步操作的协调问题

网站公告

今日签到

点亮在社区的每一天
去签到