一、Promise

1. 什么是 Promise?

Promise 是 JavaScript 中用于处理异步操作的对象,它代表一个异步操作的最终完成(或失败)及其结果值。

2. Promise 的三种状态
​​Pending(待定)​​:初始状态,既不是成功,也不是失败状态
​​Fulfilled(已兑现)​​:意味着操作成功完成
​​Rejected(已拒绝)​​:意味着操作失败
3. 创建 Promise
const myPromise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 操作成功 */) {
    resolve(value); // 将Promise状态改为fulfilled
  } else {
    reject(error);  // 将Promise状态改为rejected
  }
});
4. Promise 的方法

then()

myPromise.then(
  (value) => { /* 成功时的处理 */ },
  (error) => { /* 失败时的处理 */ }
);

catch()

myPromise.catch(
  (error) => { /* 失败时的处理 */ }
);

finally()

myPromise.finally(
  () => { /* 无论成功失败都会执行 */ }
);
5. Promise 的静态方法

Promise.resolve()

Promise.resolve(value); // 返回一个已解决的Promise

Promise.reject()

Promise.reject(reason); // 返回一个已拒绝的Promise

Promise.all()

Promise.all([promise1, promise2, ...])
  .then((values) => { /* 所有Promise都成功 */ })
  .catch((error) => { /* 任一Promise失败 */ });

Promise.race()

Promise.race([promise1, promise2, ...])
  .then((value) => { /* 第一个完成的Promise */ });

Promise.allSettled() (ES2020)

Promise.allSettled([promise1, promise2, ...])
  .then((results) => { /* 所有Promise都完成 */ });
6. Promise 链式调用
doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => console.log(`Got the final result: ${finalResult}`))
  .catch(failureCallback);

二、async/await

1. 什么是 async/await?

async/await 是建立在 Promise 之上的语法糖,让异步代码看起来更像同步代码。

2. async 函数
async function myFunction() {
  return "Hello";
}

// 等同于

function myFunction() {
  return Promise.resolve("Hello");
}
3. await 表达式
async function myFunction() {
  const result = await somePromise;
  console.log(result);
}
4. 错误处理

try/catch

async function myFunction() {
  try {
    const result = await somePromise;
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}

结合 catch()

async function myFunction() {
  const result = await somePromise.catch(error => {
    console.error(error);
    return defaultValue;
  });
  console.log(result);
}
5. 并行执行
async function myFunction() {
  // 顺序执行
  const result1 = await promise1;
  const result2 = await promise2;
  
  // 并行执行
  const [result1, result2] = await Promise.all([promise1, promise2]);
}
6. async/await 与 Promise 的对比
// 使用Promise
function getJSON() {
  return fetch('/data.json')
    .then(response => {
      if (response.status === 200) {
        return response.json();
      } else {
        throw new Error('Unable to fetch data');
      }
    })
    .then(data => console.log(data))
    .catch(err => console.log(err));
}

// 使用async/await
async function getJSON() {
  try {
    const response = await fetch('/data.json');
    if (response.status === 200) {
      const data = await response.json();
      console.log(data);
    } else {
      throw new Error('Unable to fetch data');
    }
  } catch (err) {
    console.log(err);
  }
}

三、最佳实践

1. Promise 最佳实践
​​总是返回或终止 Promise 链​​:在 then() 中总是返回新的 Promise 或 throw 错误
​​避免 Promise 嵌套​​:使用链式调用代替嵌套
​​使用 catch() 处理错误​​:不要忽略 Promise 的错误
​​命名 Promise 函数​​:提高代码可读性
2. async/await 最佳实践
​​尽量使用 try/catch​​:正确处理错误
​​避免不必要的 await​​:可以并行执行的不要顺序执行
​​考虑顶层 await​​:在模块顶层使用 await (ES2022)
​​不要混用 then() 和 async/await​​:选择一种风格并保持一致

四、高级用法

1. Promise 取消
function cancellablePromise(executor) {
  let cancel;
  const promise = new Promise((resolve, reject) => {
    executor(resolve, reject);
    cancel = reject;
  });
  promise.cancel = cancel;
  return promise;
}

const p = cancellablePromise((resolve, reject) => {
  setTimeout(() => resolve('Done'), 2000);
});

p.cancel('Operation cancelled');
2. async 生成器
async function* asyncGenerator() {
  let i = 0;
  while (i < 3) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i++;
  }
}

(async function() {
  for await (const num of asyncGenerator()) {
    console.log(num);
  }
})();
3. 顶层 await (ES2022)
// 在模块顶层直接使用await
const data = await fetch('/data.json').then(res => res.json());
console.log(data);

五、async 函数与 Promise 的关系

1. async 函数的返回值

​​不需要​​在 async 函数中显式返回 new Promise,因为 async 函数​​自动​​将返回值包装为 Promise:

async function foo() {
  return 42; // 自动包装为 Promise.resolve(42)
}

// 等同于

function foo() {
  return Promise.resolve(42);
}
2. 何时需要显式使用 new Promise

在以下情况下可能需要显式使用 new Promise:

​​包装回调式 API​​:如文件系统、网络操作、子进程、定时器、事件监听、Web API等
async function readFileAsync(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });
}

​​需要手动控制 resolve/reject 时机​​:

async function waitForEvent(element, eventName) {
  return new Promise((resolve) => {
    element.addEventListener(eventName, resolve, { once: true });
  });
}

六、async/await 与 Promise 的区别

特性Promiseasync/await
语法​​链式调用 .then().catch()类似同步代码的语法
错误处理​​使用 .catch() 或第二个参数使用 try/catch 块
​​可读性​​回调嵌套可能导致"回调地狱"线性执行,代码更清晰
调试​​调试较困难(匿名函数)调试更直观(有函数名和行号)
返回值​​总是返回 Promise总是返回 Promise
执行流程​​基于事件循环的微任务基于事件循环的微任务

七、如何配合使用 async/await 和 Promise

1. 最佳实践:混合使用场景

(1) 并行操作 + await

async function fetchAllData() {
  // 并行启动所有请求
  const [users, products, orders] = await Promise.all([
    fetch('/api/users'),
    fetch('/api/products'),
    fetch('/api/orders')
  ]);
  
  // 顺序处理结果
  const userData = await users.json();
  const productData = await products.json();
  const orderData = await orders.json();
  
  return { userData, productData, orderData };
}

(2) 需要精细控制 Promise 时

async function withTimeout(promise, ms) {
  return new Promise((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      reject(new Error('Operation timed out'));
    }, ms);
    
    promise.then(
      result => {
        clearTimeout(timeoutId);
        resolve(result);
      },
      error => {
        clearTimeout(timeoutId);
        reject(error);
      }
    );
  });
}

// 使用
try {
  const result = await withTimeout(fetch('/api/data'), 5000);
} catch (error) {
  console.error('Error:', error);
}
2. 错误处理模式对比

Promise 风格

function getUser(id) {
  return fetch(`/api/users/${id}`)
    .then(response => {
      if (!response.ok) throw new Error('Not found');
      return response.json();
    })
    .catch(error => {
      console.error('Fetch failed:', error);
      throw error; // 继续传递错误
    });
}

async/await 风格

async function getUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) throw new Error('Not found');
    return await response.json();
  } catch (error) {
    console.error('Fetch failed:', error);
    throw error; // 继续传递错误
  }
}
3. 常见配合使用模式

(1) 异步初始化模式

let initialized = false;
let initializationPromise;

async function initialize() {
  if (initialized) return;
  if (!initializationPromise) {
    initializationPromise = (async () => {
      await loadConfig();
      await connectToDB();
      initialized = true;
    })();
  }
  return initializationPromise;
}

// 使用
await initialize();
// 现在可以使用初始化后的资源

(2) 带缓存的异步请求

const cache = new Map();

async function getWithCache(url) {
  if (cache.has(url)) {
    return cache.get(url);
  }
  
  const promise = fetch(url)
    .then(response => response.json())
    .then(data => {
      cache.set(url, data);
      return data;
    });
  
  cache.set(url, promise);
  return promise;
}

// 使用
const data = await getWithCache('/api/products');
八、性能考量
​​微任务开销​​:
    await 会创建额外的微任务
    在热代码路径中避免不必要的 await

​​内存使用​​:
    每个 await 都会保留执行上下文
    长异步链可能增加内存压力

​​优化技巧​​:

// 次优:创建两个微任务
async function foo() {
  return await bar();
}
// 优化:只创建一个微任务
async function foo() {
  return bar();
}

九、常见问题与解决方案

1. Promise 常见问题

问题:忘记返回 Promise

// 错误
somePromise.then(value => {
  doSomething(value);
});

// 正确
somePromise.then(value => {
  return doSomething(value);
});

问题:忽略错误处理

// 错误
somePromise.then(value => console.log(value));

// 正确
somePromise
  .then(value => console.log(value))
  .catch(error => console.error(error));
2. async/await 常见问题

问题:忘记 await

// 错误
async function foo() {
  const result = someAsyncFunction(); // 忘记await
  console.log(result); // 输出Promise对象
}

// 正确
async function foo() {
  const result = await someAsyncFunction();
  console.log(result);
}

问题:在循环中错误使用 await

// 低效 - 顺序执行
for (const url of urls) {
  const response = await fetch(url);
  console.log(await response.json());
}

// 高效 - 并行执行
const promises = urls.map(url => fetch(url).then(res => res.json()));
const results = await Promise.all(promises);
results.forEach(result => console.log(result));

为什么我的 async 函数返回 undefined?

async function foo() {
  // 忘记 return
  Promise.resolve(42);
}
const result = await foo(); // undefined

​​解决方案​​:确保返回你想要的值(Promise 会自动包装)

async function foo() {
  return Promise.resolve(42); // 显式返回
  // 或者直接 return 42;
}

什么时候应该使用 return await?

async function foo() {
  try {
    return await riskyOperation(); // 正确:可以捕获错误
  } catch (error) {
    return fallbackValue;
  }
}

async function bar() {
  return riskyOperation(); // 错误:无法捕获错误
}

​​规则​​:

在 try/catch 块内使用 return await 以捕获错误
否则直接 return Promise 更高效(少一次微任务)

如何取消 async 函数?

function createCancellableAsyncTask(task) {
  let cancel;
  const promise = new Promise((resolve, reject) => {
    cancel = reject;
    task().then(resolve, reject);
  });
  return { promise, cancel };
}

// 使用
const { promise, cancel } = createCancellableAsyncTask(async () => {
  await longRunningOperation();
});
// 取消
cancel(new Error('User cancelled'));

十、总结

Promise 和 async/await 都是 JavaScript 中处理异步操作的重要工具:

​​Promise​​ 提供了更结构化的异步编程方式,避免了回调地狱
​​async/await​​ 让异步代码看起来更像同步代码,提高了可读性
两者可以结合使用,async 函数总是返回 Promise,await 后面可以跟任何 thenable 对象
在实际开发中,应根据场景选择合适的方式,并遵循最佳实践

JavaScript 异步编程:async/await 与 Promise 的关系与配合使用

​​async/await 是 Promise 的语法糖​​,底层仍然是 Promise
​​不需要​​在 async 函数中显式返回 new Promise,除非:
    包装回调式 API
    需要精细控制 resolve/reject 时机
​​配合使用原则​​:
    使用 async/await 处理线性异步逻辑
    使用 Promise 方法(如 Promise.all)处理并行操作
    在需要精细控制或包装旧代码时使用 new Promise
​​错误处理​​:
    try/catch 用于 async/await
    .catch() 用于 Promise 链
​​性能优化​​:
    避免不必要的 await
    合理使用并行操作
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐