async和await

138 阅读3分钟

推荐使用async和await 代替Promise

替代回调函数和 Promise 链式调用,以提高代码的可读性。而且使用 async/await 也可以很方便地控制异步请求的执行顺序。

1、顺序执行(一个接一个)

最基本的用法就是按顺序 await 每个异步操作:

async function sequentialRequests() {
  // 第一个请求先执行
  const result1 = await fetch('/api/first');
  const data1 = await result1.json();
  
  // 第一个完成后,再执行第二个
  const result2 = await fetch('/api/second');
  const data2 = await result2.json();
  
  return { data1, data2 };
}

2、并行执行后顺序处理

如果需要先发起所有请求,然后按特定顺序处理结果:

async function parallelThenSequential() {
  // 同时发起所有请求
  const promise1 = fetch('/api/first').then(res => res.json());
  const promise2 = fetch('/api/second').then(res => res.json());
  
  // 按顺序等待结果
  const data1 = await promise1;
  const data2 = await promise2;
  
  return { data1, data2 };
}

3、动态顺序执行(根据前一个请求结果决定)

async function conditionalRequests() {
  const user = await fetch('/api/user').then(res => res.json());
  
  // 根据第一个请求的结果决定是否执行第二个
  if (user.needsDetails) {
    const details = await fetch(`/api/details/${user.id}`).then(res => res.json());
    return { user, details };
  }
  
  return { user };
}

4、循环中的顺序执行

async function sequentialInLoop(urls) {
  const results = [];
  for (const url of urls) {
    const response = await fetch(url);
    results.push(await response.json());
  }
  return results;
}

5、 使用 try-catch 处理顺序请求中的错误

使用一个catch捕获替代.then().catch()

async function sequentialWithErrorHandling() {
  try {
    const result1 = await fetch('/api/first');
    const data1 = await result1.json();
    
    const result2 = await fetch('/api/second');
    const data2 = await result2.json();
    
    return { data1, data2 };
  } catch (error) {
    console.error('请求失败:', error);
    throw error; // 或者返回默认值
  }
}

结合使用Promise处理并行执行情况

虽然推荐使用 async/await,但要明白它们与 Promise 是互补关系而非替代关系

1、 并行执行后不按照顺序处理(结合 Promise 的方法)

async function recommendedApproach() {
  try {
    const [data1, data2] = await Promise.all([
       //返回r.json()
      fetch('/api/first').then(r => r.json()),
      //返回r.json()
      fetch('/api/second').then(r => r.json())
    ]);
    return { data1, data2 };
  } catch (error) {
    // 统一错误处理
    console.error('请求失败:', error);
    throw error;
  }
}

2、允许并行时,部分接口失败的情况

async function fetchMultipleAPIs() {
  const [users, posts, products] = await Promise.allSettled([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/products').then(r => r.json())
  ]);

  // 处理失败请求
  [users, posts, products].forEach(result => {
    if (result.status === 'rejected') {
      console.error('请求失败:', result.reason.message);
    }
  });

  // 返回成功的数据(失败的设为null)
  return {
    users: users.status === 'fulfilled' ? users.value : null,
    posts: posts.status === 'fulfilled' ? posts.value : null,
    products: products.status === 'fulfilled' ? products.value : null
  };
}

// 使用示例
fetchMultipleAPIs()
  .then(data => {
    console.log('获取到的数据:', data);
    // 示例输出:
    // 如果 posts 请求失败:
    // {
    //   users: [...],
    //   posts: null,
    //   products: [...]
    // }
  });

3、并行请求的性能优化建议

对于大量并行请求,建议增加并发控制:

async function parallelWithLimit(urls, limit = 5) {
  const results = [];
  
  for (let i = 0; i < urls.length; i += limit) {
    const chunk = urls.slice(i, i + limit);
    const chunkResults = await Promise.allSettled(
      chunk.map(url => fetch(url).then(r => r.json()))
    );
    results.push(...chunkResults);
  }
  
  return results;
}

4、取消请求的支持

结合 AbortController 实现可取消的请求, 这个可以不考虑一般已经封装在了项目的request中

async function cancellableFetch(url, { signal } = {}) {
  const response = await fetch(url, { signal });
  if (signal?.aborted) throw new Error('请求被取消');
  return response.json();
}

// 使用示例
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // 5秒后取消

try {
  const data = await cancellableFetch('/api/data', { 
    signal: controller.signal 
  });
} catch (err) {
  if (err.message === '请求被取消') {
    console.log('主动取消请求');
  }
}

关键点总结

  1. await 会暂停函数执行,直到 Promise 解决,所以顺序排列的 await 会按顺序执行
  2. 如果不关心前一个请求的结果,只是要确保顺序,可以直接顺序 await
  3. 如果需要提高性能,可以先并行发起请求,然后顺序处理结果
  4. 错误处理很重要,可以使用 try-catch 包裹多个顺序请求

记住,async/await 只是 Promise 的语法糖,本质上还是在操作 Promise。选择顺序执行还是并行执行取决于业务需求和对性能的要求。