回调地狱到优雅异步:ES6 Promise完全指南

93 阅读3分钟

引言:异步编程的"前世今生"

作为前端开发者,你是否也曾面对这样的代码?

// 回调地狱示例
setTimeout(() => {
  console.log('第一层');
  setTimeout(() => {
    console.log('第二层');
    setTimeout(() => {
      console.log('第三层');
      // ...无尽嵌套
    }, 1000);
  }, 1000);
}, 1000);

这种"回调地狱"不仅可读性差,维护更是噩梦。ES6的Promise正是为解决异步流程控制而生,它让异步代码变得线性、可预测

一、Promise基础:认识异步承诺

1.1 Promise是什么?

Promise是一个 异步操作的容器 ,它代表了一个最终可能完成(或失败)的异步操作及其结果值。想象成你点外卖:下单(创建Promise)→制作中(pending)→送达(fulfilled)/取消(rejected)。

1.2 基本语法

// 创建Promise实例
const p = new Promise((resolve) => {
  console.log('3333'); // 同步执行
  setTimeout(() => {
    console.log('2222');
    resolve(); // 异步操作完成后调用
  }, 10);
});

// 通过then处理结果
p.then(() => {
  console.log('1111');
});

执行顺序:3333 → 2222 → 1111,完美解决了异步代码的执行顺序问题

1.3 三种状态

  • pending :初始状态,既不是成功也不是失败
  • fulfilled :操作成功完成,调用resolve()
  • rejected :操作失败,调用reject()

注意:Promise状态一旦改变就不可逆!就像泼出去的水,无法收回

二、核心方法:then的链式魔法

2.1 基础用法

Promise实例的 then 方法用于注册回调函数,处理成功状态的值:

// 文件读取示例
const fs = require('fs');
const readFilePromise = new Promise((resolve) => {
  fs.readFile('./1.html', (err, data) => {
    console.log(data.toString());
    resolve(); // 读取完成后触发then
  });
});

readFilePromise.then(() => {
  console.log('读完啦======');
});

2.2 链式调用

then 方法会返回一个新的Promise,这让链式调用成为可能:

fetchData()
  .then(data => process(data))
  .then(result => display(result))
  .then(() => console.log('完成'));

每个then都接收前一个then的返回值,形成清晰的流水线。

三、语法糖:async/await让异步变同步

ES2017引入的async/await是Promise的语法糖,让异步代码看起来像同步代码:

3.1 基本用法

(async function(){
  const p = new Promise((resolve) => {
    setTimeout(() => {
      resolve('success');
    }, 1000);
  });
  const res = await p; // 等待Promise完成
  console.log(res); // success
  console.log('111111');
})();

3.2 实战案例:GitHub API请求

document.addEventListener('DOMContentLoaded', async () => {
  const res = await fetch('https://api.github.com/users/NFkenny/repos');
  const data = await res.json(); // 链式await
  document.getElementById('repos').innerHTML = data.map(item => {
    return `<li><a href="${item.html_url}" target="_blank">${item.name}</a></li>`
  }).join('');
});

四、常见陷阱与最佳实践

4.1 错误处理

// 使用catch捕获错误
fetchData()
  .then(processData)
  .catch(error => {
    console.error('出错啦:', error);
  });

// async/await中使用try-catch
async function loadData() {
  try {
    const data = await fetchData();
  } catch (error) {
    console.error(error);
  }
}

4.2 并行处理

// 使用Promise.all处理并行任务
Promise.all([
  fetchUserInfo(),
  fetchOrderList(),
  fetchProductList()
]).then(([user, orders, products]) => {
  // 所有请求都完成后执行
});

五、Promise的哲学启示

通过上述示例我们可以发现:

  1. Promise将异步操作 对象化 ,让流程控制更直观
  2. then方法实现了 责任链模式 ,每个环节专注单一职责
  3. async/await是 语法层面的抽象 ,底层仍是Promise机制

这种设计哲学深刻影响着现代前端架构,从Redux的状态管理到微前端通信机制,都能看到Promise思想的延伸。其核心价值在于: 用确定性的语法结构驾驭不确定性的异步世界