Promise、Generator、Async三者的区别

简介: 掌握这一篇就够了

theme: cyanosis

在 JavaScript 中,我们经常需要处理异步操作,例如从服务器获取数据或执行耗时的计算。

背景

在Promise、Generator、Async出现之前,JavaScript处理异步都是通过回调(callback)来实现的。

function fetchData(callback) {
   
  // 模拟异步操作,比如发送请求到服务器获取数据
  setTimeout(() => {
   
    const data = {
    message: 'Data fetched successfully' };
    // 模拟成功响应,将数据传递给回调函数
    callback(null, data);
    // 模拟失败响应,将错误传递给回调函数
    // callback(new Error('Failed to fetch data'), null);
  }, 2000);
}

// 定义一个回调函数来处理异步操作的结果
function handleData(error, data) {
   
  if (error) {
   
    console.log(error.message);
  } else {
   
    console.log(data.message);
  }
}

// 调用fetchData函数并传递回调函数
fetchData(handleData);

然而,回调(callback)存在着几个致命缺陷:

  1. 回调地狱(Callback Hell):当存在多个嵌套的异步操作时,使用回调函数来处理它们会导致代码的嵌套层级变得非常深,代码可读性和可维护性降低。
  2. 错误处理困难:由于异步操作无法通过传统的 try-catch 语句捕获异常,需要在回调函数中手动检查错误,并采取适当的处理措施,这种错误处理方式容易遗漏错误。
  3. 可读性和维护性差:回调函数的嵌套和复杂的控制流,在处理复杂的异步操作时,代码结构变得混乱,难以追踪和调试。

所以为了更好地管理和控制异步代码,ES6(ECMAScript 2015)引入了三种重要的概念:Promise、Generator和Async/Await。本文将探讨它们之间的区别和使用场景。

Promise

Promise是一种处理异步操作的机制,它具有三种状态:pending(进行中)、resolved(成功)和rejected(失败)。通过调用thencatch方法,我们可以处理异步操作的结果或错误。

function fetchData() {
   
  return new Promise((resolve, reject) => {
   
    // 模拟异步操作,比如发送请求到服务器获取数据
    setTimeout(() => {
   
      const data = {
    message: 'Hello, world!' };
      // 模拟成功响应
      resolve(data);
      // 模拟失败响应
      // reject(new Error('Failed to fetch data'));
    }, 2000);
  });
}

// 调用fetchData函数并使用Promise的then方法处理异步操作的结果
fetchData()
  .then((data) => {
   
    console.log(data.message);
  })
  .catch((error) => {
   
    console.log(error.message);
  });

Promise的优点是它提供了一种结构化的方式来管理异步代码,避免了回调地狱(callback hell)的问题。我们可以链式调用多个 Promise 来按顺序执行异步操作,使代码更加清晰和可读。

但是 Promise 缺点也很明显:

  1. 无法取消 Promise,一旦创建就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误不会传递到外部。
  3. 当处于 pending 状态时,无法得知目前进展到哪个阶段(刚刚开始还是即将完成)。
  4. Promise 的错误堆栈上下文不太友好,因为在真正执行回调时,定义 Promise 的部分已经执行完毕。

Generator

Generator 是 ES6 引入的新语法,它是一种可以暂停和继续执行的函数。通过使用yield关键字,我们可以在函数中暂停执行并返回一个值。

简单的用法,可以当做一个Iterator可迭代器来用,用于进行遍历操作。复杂一些的用法是在内部保存一些状态,成为一个状态机。

Generator 基本语法包含两部分:函数名前要加一个星号;函数内部用 yield 关键字返回值。

  • yield,表达式本身没有返回值,或者说总是返回 undefined。
  • next,方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
function * foo(x) {
   
    var y = 2 * (yield (x + 1));
    var z = yield (y / 3);
    return (x + y + z);
}
var b = foo(5); 
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false } 
b.next(13) // { value:42, done:true }

Generator 的优点在于它提供了一种更灵活的控制流程的方式。我们可以使用yield来控制函数的执行,根据需要决定是否继续执行下一步。这种能力可以用于实现异步操作的顺序控制,使得代码更具可读性。

然而,Generator的语法相对复杂,使用时需要手动迭代生成器并调用next方法。它也无法直接处理异步操作,需要结合其他机制(如回调函数)来实现。

Async(推荐使用~~)

Async 是 Generator 的一个语法糖。通过在函数前添加async关键字,我们可以定义一个异步函数。在函数内部,使用await关键字可以暂停函数的执行,等待一个 Promise 的解决结果。

与 Generator 相比之下,async对应的是*await对应的是yield。async/await 同时也自动进行了 Generator 的流程控制。

async function fetchUser() {
   
  const user = await ajax()
  console.log(user)
}

不难看出,使用 Async/Await 的优点在于它让异步代码的编写和理解更加简洁。

但是也需要注意,若明确需要在当前函数内部执行异步转同步操作时,再使用async。原因是 Babel 会将 async 编译成 Promise,导致编译后的代码量增加(而且过多的异步操作同时执行,也会降低性能)。

为什么Async/Await更好?

  1. 使用Async/Await可以让代码更简洁易读。相比于Promise,不需要使用多个then方法和匿名函数来处理resolve值,也无需定义额外的变量来存储数据。而与Generator相比,不需要手动调用next方法进行流程控制。总之,Async/Await使得代码简洁易读,避免了嵌套的代码结构。

  2. Async/Await还提供了更便捷的错误处理方式,通过try/catch可以同时处理同步和异步错误,使得错误处理更加简洁明了。

目录
相关文章
|
1月前
|
前端开发 JavaScript 开发者
Async 和 Await 是基于 Promise 实现
【10月更文挑战第30天】Async和Await是基于Promise实现的语法糖,它们通过简洁的语法形式,借助Promise的异步处理机制,为JavaScript开发者提供了一种更优雅、更易于理解和维护的异步编程方式。
26 1
|
17天前
|
前端开发
Promise.allSettled()方法和Promise.race()方法有什么区别?
`Promise.allSettled()` 提供了一种更全面、更详细的方式来处理多个 `Promise`,而 `Promise.race()` 则更强调速度和竞争。我们需要根据具体的需求来选择使用哪种方法。
|
24天前
|
前端开发
如何使用async/await解决Promise的缺点?
总的来说,`async/await` 是对 Promise 的一种很好的补充和扩展,它为我们提供了更高效、更易读、更易维护的异步编程方式。通过合理地运用 `async/await`,我们可以更好地解决 Promise 的一些缺点,提升异步代码的质量和开发效率。
33 5
|
24天前
|
前端开发 JavaScript
async/await和Promise在性能上有什么区别?
性能优化是一个综合性的工作,除了考虑异步模式的选择外,还需要关注代码的优化、资源的合理利用等方面。
37 4
|
1月前
|
JSON 前端开发 JavaScript
浅谈JavaScript中的Promise、Async和Await
【10月更文挑战第30天】Promise、Async和Await是JavaScript中强大的异步编程工具,它们各自具有独特的优势和适用场景,开发者可以根据具体的项目需求和代码风格选择合适的方式来处理异步操作,从而编写出更加高效、可读和易于维护的JavaScript代码。
31 1
|
2月前
|
前端开发 JavaScript
setTimeout、Promise、Async/Await 的区别
`setTimeout` 是用于延迟执行函数的简单方法;`Promise` 表示异步操作的最终完成或失败;`Async/Await` 是基于 Promise 的语法糖,使异步代码更易读和维护。三者都用于处理异步操作,但使用场景和语法有所不同。
|
2月前
|
前端开发 JavaScript 开发者
JavaScript 中的异步编程:深入了解 Promise 和 async/await
【10月更文挑战第8天】JavaScript 中的异步编程:深入了解 Promise 和 async/await
|
2月前
|
前端开发 JavaScript UED
深入了解JavaScript异步编程:回调、Promise与async/await
【10月更文挑战第11天】深入了解JavaScript异步编程:回调、Promise与async/await
22 0
|
3月前
|
前端开发 JavaScript
解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!
该文章教授了如何使用Promise和async/await来解决异步编程问题,从而避免回调地狱,使代码更加清晰和易于管理。
解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!
|
4月前
|
前端开发 JavaScript 开发者
探索前端开发中的异步编程:Promise与Async/Await
在现代前端开发中,处理异步操作是至关重要的。本文将深入探讨异步编程的核心概念,重点比较JavaScript中的Promise与Async/Await两种异步编程方式。通过实例和比较,读者将能够理解这两种方法的优缺点,如何在实际开发中选择适合的异步编程模式,从而编写更简洁、可维护的代码。

热门文章

最新文章