Promise与async/await

Promiseasync/await 是 JavaScript 中处理异步操作的两种重要方式。

一、基本语法

Promise

Promise 是一种表示某个异步操作结果的对象。它有三种状态:

  • pending(进行中):初始状态,既不是成功,也不是失败。
  • fulfilled(已成功):操作成功完成。
  • rejected(已失败):操作失败。

Promise 的基本用法如下:

const promise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 操作成功 */) {
    resolve(value); // 将 Promise 状态改为 fulfilled,并将结果作为参数传递给后续的 then 方法
  } else {
    reject(error); // 将 Promise 状态改为 rejected,并将错误信息作为参数传递给后续的 catch 方法
  }
});

promise
  .then(value => {
    // 处理成功的结果
  })
  .catch(error => {
    // 处理失败的结果
  });

async/await

async/await 是基于 Promise 实现的语法糖,让异步操作的代码看起来更像同步代码。async 函数返回一个 Promise 对象,await 关键字用于等待 Promise 的结果。

基本用法如下:

// 使用 async 关键字声明一个异步函数
async function asyncFunction() {
  try {
    // 使用 await 关键字等待 Promise 结果
    const result = await somePromiseFunction();
    // 处理成功的结果
  } catch (error) {
    // 处理失败的结果
  }
}

// 调用异步函数
asyncFunction();

区别与联系

  • Promise 是一种异步操作的表示方式,而 async/await 是基于 Promise 的语法糖,让异步代码更易读、易写。
  • Promise 需要使用 .then().catch() 方法处理结果,而 async/await 使用 try/catch 语句处理结果。
  • Promise 可以在多个地方使用 .then() 方法链式处理,而 async/await 只能在 async 函数内部使用。

总结:async/await 是基于 Promise 的语法糖,它简化了异步操作的处理方式,使代码更加简洁易读。

二、举例

下面是一个完整的例子,展示了如何使用 Promiseasync/await 来处理异步操作。我们将模拟一个简单的场景:从服务器获取用户信息,然后根据用户 ID 获取用户的帖子。

1、使用 Promise
// 模拟异步请求函数
function fetchUserInfo(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ id: userId, name: 'John Doe' });
    }, 1000);
  });
}

function fetchUserPosts(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([
        { id: 1, title: 'Post 1' },
        { id: 2, title: 'Post 2' },
      ]);
    }, 1000);
  });
}

// 使用 Promise 链式调用
function getUserPostsPromise(userId) {
  fetchUserInfo(userId)
    .then(userInfo => {
      console.log('User Info:', userInfo);
      return fetchUserPosts(userInfo.id);
    })
    .then(posts => {
      console.log('User Posts:', posts);
    })
    .catch(error => {
      console.error('Error:', error);
    });
}

// 调用函数
getUserPostsPromise(1);
2、使用 async/await
// 同样的模拟异步请求函数
function fetchUserInfo(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ id: userId, name: 'John Doe' });
    }, 1000);
  });
}

function fetchUserPosts(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([
        { id: 1, title: 'Post 1' },
        { id: 2, title: 'Post 2' },
      ]);
    }, 1000);
  });
}

// 使用 async/await
async function getUserPostsAsync(userId) {
  try {
    const userInfo = await fetchUserInfo(userId);
    console.log('User Info:', userInfo);

    const posts = await fetchUserPosts(userInfo.id);
    console.log('User Posts:', posts);
  } catch (error) {
    console.error('Error:', error);
  }
}

// 调用函数
getUserPostsAsync(1);
3、解释
  1. 模拟异步请求

    • fetchUserInfofetchUserPosts 函数使用 setTimeout 来模拟异步请求,返回一个 Promise
  2. 使用 Promise

    • getUserPostsPromise 函数通过链式调用 .then() 来处理异步操作的结果,并使用 .catch() 来捕获错误。
  3. 使用 async/await

    • getUserPostsAsync 函数使用 async 关键字声明为异步函数。
    • 在函数内部,使用 await 关键字等待 Promise 的结果,使代码看起来像同步代码。
    • 使用 try/catch 块来捕获和处理错误。

三、对比

Promise和async/await在代码可读性上存在以下区别:

(一)Promise的可读性问题

  1. 回调地狱风险

    • 当有多个连续的异步操作且每个操作依赖于前一个操作的结果时,使用Promise可能会导致类似“回调地狱”的情况。例如:
      doSomething()
       .then(result1 => {
          return doSomethingElse(result1);
        })
       .then(result2 => {
          return yetAnotherThing(result2);
        })
       .then(finalResult => {
          console.log('Got the final result:', finalResult);
        })
       .catch(error => {
          console.error('Error:', error);
        });
      
    • 随着异步操作的增加,代码会不断地嵌套.then()方法,使得代码结构变得复杂,难以一眼看清整个流程。
  2. 错误处理的复杂性

    • 在Promise链中,错误处理需要在每个.then()后面添加.catch(),如果忘记添加或者在复杂的链式调用中,可能会导致错误难以排查。例如:
      doSomething()
       .then(result1 => {
          //一些操作
          return doSomethingElse(result1);
        })
       .then(result2 => {
          //这里如果抛出错误,可能不会被正确捕获
          throw new Error('Something went wrong');
        })
       .then(result3 => {
          //...
        });
      

(二)async/await提升可读性的表现

  1. 顺序执行的直观表达

    • async/await允许以同步的方式编写异步代码,使得异步操作的顺序执行非常直观。例如:
      async function myAsyncFunction() {
        const result1 = await doSomething();
        const result2 = await doSomethingElse(result1);
        const finalResult = await yetAnotherThing(result2);
        console.log('Got the final result:', finalResult);
      }
      
    • 这里代码看起来就像是在按顺序执行同步操作,更容易理解整个异步流程。
  2. 统一的错误处理

    • 使用async/await结合try/catch可以轻松地对整个异步操作过程中的错误进行统一处理。例如:
      async function myAsyncFunction() {
        try {
          const result1 = await doSomething();
          const result2 = await doSomethingElse(result1);
          const finalResult = await yetAnotherThing(result2);
          console.log('Got the final result:', finalResult);
        } catch (error) {
          console.error('Error:', error);
        }
      }
      
    • 不需要在每个异步操作后面单独处理错误,提高了代码的可维护性和可读性。

总的来说,async/await在代码可读性上通常优于Promise,特别是在处理复杂的异步操作流程和错误处理时。然而,Promise也有其自身的优势,例如在一些需要并行处理异步操作的场景下,Promise.all()等方法使用起来更加方便。

四、兼容性

Promise 和 async/await 在浏览器兼容性上的差异主要体现在对旧版浏览器的支持上。

1、Promise 的兼容性

  • 现代浏览器支持:大多数现代浏览器(如 Chrome、Firefox、Edge、Safari)都支持 Promise,包括其相关方法如 Promise.allPromise.finally
  • 旧版浏览器问题:然而,一些旧版本的浏览器(如 Internet Explorer)可能不支持 Promise 或只支持部分功能。例如,IE11 不支持 Promise.allSettledPromise.finally,并且对 .then 方法的支持也不完全。

2、async/await 的兼容性

  • 基于 Promise:async/await 是基于 Promise 的语法糖,因此它的兼容性问题与 Promise 相似。如果浏览器不支持 Promise,async/await 也无法正常工作。
  • 转译和 Polyfill:为了在旧版浏览器中使用 async/await,开发者可以使用 Babel 等工具将代码转译为 ES5,或者使用 polyfill 库(如 core-js)来填补 Promise 和 async/await 的兼容性问题。

3、总结

  • Promise:在现代浏览器中广泛支持,但在旧版浏览器中可能需要 polyfill 或转译工具。
  • async/await:依赖于 Promise,因此在现代浏览器中支持良好,但在旧版浏览器中同样需要转译或 polyfill 来确保兼容性。

你可能感兴趣的:(前端技术,后端技术,javascript,promise,async,await)