1. 可读性与代码结构(最核心的优势)

Promise(链式调用 .then ):

javascript

function fetchUserData(userId) {
  return fetch(`/api/users/${userId}`)
    .then(response => {
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json(); // 返回一个 Promise,解析为 JSON 数据
    })
    .then(user => {
      return fetch(`/api/posts?userId=${user.id}`);
    })
    .then(postsResponse => postsResponse.json())
    .then(posts => {
      console.log('User posts:', posts);
      return posts;
    })
    .catch(error => {
      console.error('There was a problem with the fetch operation:', error);
    });
}

代码是“横向”发展的,形成了一个长长的链条。如果逻辑复杂,需要嵌入很多条件判断,代码会变得难以阅读和维护。

Async/Await:

javascript

async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    const user = await response.json();

    const postsResponse = await fetch(`/api/posts?userId=${user.id}`);
    const posts = await postsResponse.json();

    console.log('User posts:', posts);
    return posts;
  } catch (error) {
    console.error('There was a problem with the fetch operation:', error);
  }
}

代码是“纵向”发展的,就像写同步代码一样。执行流程非常清晰:先等这个完成,再做这个,然后做那个。这大大降低了理解和调试的难度。

2. 错误处理

Promise: 使用 .catch() 方法来捕获链条中任何一个环节的错误。虽然很强大,但有时很难精确定位错误发生的位置。

Async/Await: 可以使用经典的 try...catch 块来捕获错误,这是开发者非常熟悉的同步代码错误处理机制,非常直观。

javascript

// Async/Await 的错误处理更符合直觉
async function myFunction() {
  try {
    const result1 = await asyncTask1();
    const result2 = await asyncTask2(result1);
    return result2;
  } catch (error) {
    // 可以在这里统一处理 asyncTask1 或 asyncTask2 中抛出的错误
    console.error('Something failed:', error);
    // 也可以轻松地在这里进行错误恢复或重试逻辑
  }
}

3. 条件判断和中间值处理

当异步操作的结果需要参与条件判断,或者一个异步操作的结果需要作为多个后续操作的参数时,Promise 会显得非常笨拙。

Promise(繁琐的例子):

javascript

function makeRequest() {
  return getJSON()
    .then(data => {
      // 我们需要 data 来做判断
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(otherData => {
            console.log(otherData);
            return otherData;
          });
      } else {
        // 直接返回一个值,以便链条继续
        return data;
      }
    });
}

我们需要在 .then 内部进行分支,并且要确保每个分支都返回一个值,以保持 Promise 链条的连续性。

Async/Await(清晰明了):

javascript

async function makeRequest() {
  const data = await getJSON();
  if (data.needsAnotherRequest) {
    const otherData = await makeAnotherRequest(data);
    console.log(otherData);
    return otherData;
  } else {
    return data;
  }
}

条件逻辑和同步代码完全一样,没有任何心智负担。

4. 调试

Promise: 由于代码被分割在多个 .then 回调中,当你在 .then 块中设置断点时,调用栈(Call Stack)可能不连贯,调试起来会比较困难。

Async/Await: 因为引擎将其视为同步执行流,调用栈是清晰且连续的。调试时就像调试同步代码一样,可以轻松地一步步执行。

总结:为什么需要 Async/Await?

特性 Promise Async/Await
可读性 链式调用,横向发展,复杂逻辑下晦涩 同步写法,纵向发展,流程清晰
错误处理 .catch() try...catch,更直观
条件/中间逻辑 需要在 .then 中处理,较繁琐 与同步代码写法一致,非常简单
调试 调用栈不清晰 调用栈清晰,易于调试

结论:

async/await 并没有取代 Promise,而是建立在 Promise 之上,提供了一种更优雅、更符合人类线性思维的编码方式。它解决了 Promise 在复杂异步流程中带来的“回调地狱”变体(即冗长的 .then 链条)问题。

你可以把它们的关系理解为:

  • Promise 是异步操作的标准化容器和协议。

  • async/await 是利用这个协议,让异步代码写起来更舒服的“语法糖”和“新语法”。

Logo

一座年轻的奋斗人之城,一个温馨的开发者之家。在这里,代码改变人生,开发创造未来!

更多推荐