在 JavaScript 中,Promise 是一种用于处理异步操作的对象,而 fetch 是现代浏览器提供的用于发起网络请求的 API,fetch API 本身返回的就是一个 Promise 对象,这使得我们可以优雅地处理网络请求的异步响应。

1. Promise 复习

Promise 有三种状态:

  • ending(进行中)

  • fulfilled(已成功)

  • rejected(已失败)

基本用法:

const promise = new Promise((resolve, reject) => {
  // 异步操作代码
  if (操作成功) {
    resolve(结果);
  } else {
    reject(错误);
  }
});

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

2. fetch API 基础

fetch() 是一个全局方法,用于发起 HTTP 请求,返回一个 Promise 对象,该 Promise 会在请求响应后被解析。

基本语法:

fetch(url, options)
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json(); // 解析JSON数据
  })
  .then(data => {
    console.log(data); // 使用解析后的数据
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

3. fetch 返回 Promise 的特性

fetch 返回的 Promise 在以下情况会被解析:

  • 当接收到服务器响应时(即使响应状态码表示错误,如 404 或 500)。

  • 只有当网络错误阻止请求完成时才会被拒绝(如断网)。

重要特性:fetch 的 Promise 不会因为 HTTP 错误状态码(如 404、500)而被拒绝,只有当网络请求本身失败时才会拒绝。因此,必须手动检查 response.ok 属性

4. Promise 与 fetch 结合使用的高级场景

4.1 处理不同类型的响应
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    // 根据 Content-Type 决定如何解析响应
    const contentType = response.headers.get('content-type');
    if (contentType && contentType.includes('application/json')) {
      return response.json();
    } else if (contentType && contentType.includes('text/plain')) {
      return response.text();
    }
    throw new Error('Unsupported content type');
  })
  .then(data => {
    console.log('处理后的数据:', data);
  })
  .catch(error => {
    console.error('There was a problem with the fetch operation:', error);
  });
4.2 使用 async/await 简化 fetch 代码
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    console.log(data);
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
    throw error; // 可以选择继续抛出以便上层处理
  }
}

// 调用函数
fetchData().then(result => {
  // 使用结果
}).catch(err => {
  // 处理错误
});
4.3 并行发起多个 fetch 请求

利用 Promise.all() 可以并行发起多个网络请求:

const fetchUser = fetch('https://api.example.com/user');
const fetchPosts = fetch('https://api.example.com/posts');
const fetchComments = fetch('https://api.example.com/comments');

Promise.all([fetchUser, fetchPosts, fetchComments])
  .then(responses => {
    // 确保所有响应都成功
    const validResponses = responses.map(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return response.json();
    });
    
    // 等待所有数据解析完成
    return Promise.all(validResponses);
  })
  .then(([userData, postsData, commentsData]) => {
    // 同时获取到了所有数据
    console.log('用户数据:', userData);
    console.log('帖子数据:', postsData);
    console.log('评论数据:', commentsData);
  })
  .catch(error => {
    console.error('至少一个请求失败:', error);
  });

5. fetch 的常见配置选项

fetch('https://api.example.com/data', {
  method: 'POST', // GET, POST, PUT, DELETE 等
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your-token-here'
  },
  body: JSON.stringify({ key: 'value' }), // 仅用于 POST, PUT 等
  credentials: 'include', // 包含 cookies
  cache: 'no-cache',
  redirect: 'follow',
  referrerPolicy: 'no-referrer'
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

6. 总结

  • Promise 是处理异步操作的基础,提供了一种优雅的方式来处理异步代码。

  • fetch 是基于 Promise 的网络请求 API,返回 Promise 对象。

  • fetch 的 Promise 仅在网络错误时拒绝,HTTP 错误状态码需要手动处理。

  • 结合 async/await 可以使 fetch 代码更加简洁易读。

  • Promise.all() 等静态方法可以与 fetch 结合,实现并行请求等高级场景。

通过 Promise 和 fetch 的结合使用,我们可以编写更加清晰、可维护的异步网络请求代码。