明明有了promise,为啥还需要async await?

更新日期: 2021-05-11阅读: 1.2k标签: async

为了让还没听说过这个特性的小伙伴们有一个大致了解,以下是一些关于该特性的简要介绍:

async/await是一种编写异步代码的新方法。在这之前编写异步代码使用的是回调函数和promise。
async/await实际是建立在promise之上的。因此你不能把它和回调函数搭配使用。
async/await可以使异步代码在形式上更接近于同步代码。这就是它最大的价值。

语法

假设有一个getJSON方法,它返回一个promise,该promise会被resolve为一个JSON对象。我们想要调用该方法,输出得到的JSON对象,最后返回"done"。

以下是使用promise的实现方式:

const makeRequest = () =>
  getJSON()
    .then(data => {
      console.log(data)
      return "done"
    })
makeRequest()

使用async/await则是这样的:

const makeRequest = async () => {
  console.log(await getJSON())
  return "done"
}

makeRequest()

使用async/await时有以下几个区别:

在定义函数时我们使用了async关键字。await关键字只能在使用async定义的函数的内部使用。所有async函数都会返回一个promise,该promise最终resolve的值就是你在函数中return的内容。
由于第一点中的原因,你不能在顶级作用域中await一个函数。因为顶级作用域不是一个async方法。

// this will not work in top level
// await makeRequest()
    
// this will work
makeRequest().then((result) => {
  // do something
})

await getJSON()意味着直到getJSON()返回的promise在resolve之后,console.log才会执行并输出resolove的值。


为何使用async/await编写出来的代码更好呢?

1. 简洁

看看我们节省了多少代码吧。即使是在这么一个简单的例子中,我们也节省了可观的代码。我们不需要为.then编写一个匿名函数来处理返回结果,也不需要创建一个data变量来保存我们实际用不到的值。我们还避免了代码嵌套。这些小优点会在真实项目中变得更加明显。


2. 错误处理

async/await终于使得用同一种构造(古老而好用的try/catch) 处理同步和异步错误成为可能。在下面这段使用promise的代码中,try/catch不能捕获JSON.parse抛出的异常,因为该操作是在promise中进行的。要处理JSON.parse抛出的异常,你需要在promise上调用.catch并重复一遍异常处理的逻辑。通常在生产环境中异常处理逻辑都远比console.log要复杂,因此这会导致大量的冗余代码。

const makeRequest = () => {
    try {
    getJSON()
      .then(result => {
        // this parse may fail
        const data = JSON.parse(result)
        console.log(data)
      })
      // uncomment this block to handle asynchronous errors
      // .catch((err) => {
      //   console.log(err)
      // })
    } catch (err) {
        console.log(err)
    }
}

现在看看使用了async/await的情况,catch代码块现在可以捕获JSON.parse抛出的异常了:

const makeRequest = async () => {
  try {
    // this parse may fail
    const data = JSON.parse(await getJSON())
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}


3. 条件分支

假设有如下逻辑的代码。请求数据,然后根据返回数据中的某些内容决定是直接返回这些数据还是继续请求更多数据:

const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData
          })
      } else {
        console.log(data)
        return data
      }
    })
}

只是阅读这些代码已经够让你头疼的了。一不小心你就会迷失在这些嵌套(6层),空格,返回语句中。

在使用async/await改写后,这段代码的可读性大大提高了:

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


4. 中间值

你可能会遇到这种情况,请求promise1,使用它的返回值请求promise2,最后使用这两个promise的值请求promise3。对应的代码看起来是这样的:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      // do something
      return promise2(value1)
        .then(value2 => {
          // do something          
          return promise3(value1, value2)
        })
    })
}

如果promise3没有用到value1,那么我们就可以把这几个promise改成嵌套的模式。如果你不喜欢这种编码方式,你也可以把value1和value2封装在一个Promsie.all调用中以避免深层次的嵌套:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      // do something
      return Promise.all([value1, promise2(value1)])
    })
    .then(([value1, value2]) => {
      // do something          
      return promise3(value1, value2)
    })
}

这种方式为了保证可读性而牺牲了语义。除了避免嵌套的promise,没有其它理由要把value1和value2放到一个数组里。

同样的逻辑如果换用async/await编写就会非常简单,直观。

const makeRequest = async () => {
  const value1 = await promise1()
  const value2 = await promise2(value1)
  return promise3(value1, value2)
}

5. 异常堆栈

假设有一段串行调用多个promise的代码,在promise串中的某一点抛出了异常:

const makeRequest = () => {
  return callAPromise()
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => {
      throw new Error("oops");
    })
}

makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
  })

从promise串返回的异常堆栈中没有包含关于异常是从哪一个环节抛出的信息。更糟糕的是,它还会误导你,它包含的唯一的函数名是callAPromise,然而该函数与此异常并无关系。(这种情况下文件名和行号还是有参考价值的)。

然而,在使用了async/await的代码中,异常堆栈指向了正确的函数:

const makeRequest = async () => {
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  throw new Error("oops");
}

makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at makeRequest (index.js:7:9)
  })

这带来的好处在本地开发环境中可能并不明显,但当你想要在生产环境的服务器上获取有意义的异常信息时,这会非常有用。在这种情况下,知道异常来自makeRequest而不是一连串的then调用会有意义的多。


6. 调试

最后压轴的一点,使用async/await最大的优势在于它很容易被调试。由于以下两个原因,调试promise一直以来都是很痛苦的。

你不能在一个返回表达式的箭头函数中设置断点(因为没有代码块)

 

如果你在一个.then代码块中使用调试器的步进(step-over)功能,调试器并不会进入后续的.then代码块,因为调试器只能跟踪同步代码的『每一步』。

通过使用async/await,你不必再使用箭头函数。你可以对await语句执行步进操作,就好像他们都是普通的同步调用一样。

 

结论 async/await是过去几年中JavaScript引入的最具革命性的特性之一。它使你意识到promise在语法上的糟糕之处,并提供了一种简单,直接的替代方案。

参考文章
作者:Angus安格斯
链接:https://juejin.cn/post/6960855679208783903
来源:掘金


链接: https://www.fly63.com/article/detial/10283

JS中的async/await的执行顺序详解

虽然大家知道async/await,但是很多人对这个方法中内部怎么执行的还不是很了解,本文是我看了一遍技术博客理解 JavaScript 的 async/await

ES6新特性Async

Async实际上是一个封装了自动化执行并返回一个Promise的Generator函数的语法糖。这句话的意思我们可以分为三个部分来解读:首先它有一个自动化执行,Generator函数是依靠不停的调用.net来依次执行的,Async有一个自动化执行的过程

Node.js Async 函数最佳实践

Node.js7.6起, Node.js 搭载了有async函数功能的V8引擎。当Node.js 8于10月31日成为LTS版本后,我们没有理由不使用async函数。接下来,我将简要介绍async函数,以及如何改变我们编写Node.js应用程序的方式。

将async/await编译到 ES3/ES5 (外部帮助库)

自2015年11 发布1.7版以来,TypeScript 已支持 async/await 关键字。编译器使用 yield 将异步函数转换为生成器函数。这意味着咱们无法针对 ES3 或 ES5,因为生成器仅在 ES6 中引入的。

你可能忽略的 async/await 问题

async/await 大家肯定都用过,在处理异步操作的时候真的是很方便。那今天主要讲一些在使用 async/await 时容易忽略和犯错的地方。上面的代码中,每一行都会 等待上一行的结果返回后才会执行。

使用async await通过for循环在图片onload加载成功后获取成功的图片地址

有一个图片列表,我想要在图片onload成功之后获取加载成功的图片列表,图片资源加载为异步,我们使用ES7的async await方式实现,多张图片,是用for循环。

ES8 Async 和 Await

Async 和 Awaiit 是 Promise 的扩展,我们知道 JavaScript 是单线程的,使用 Promise 之后可以使异步操作的书写更简洁,而 Async 使 Promise 像同步操作

有了 Promise 和 then,为什么还要使用 async?

最近代码写着写着,我突然意识到一个问题——我们既然已经有了 Promise 和 then,为啥还需要 async 和 await?这不是脱裤子放屁吗?

手写async await的最简实现

如果让你手写async函数的实现,你是不是会觉得很复杂?这篇文章带你用20行搞定它的核心。经常有人说async函数是generator函数的语法糖,那么到底是怎么样一个糖呢?让我们来一层层的剥开它的糖衣。

认识async/await

async/await是ES7的写法,可以让非同步call back写法看起来像同步的顺序去执行。以下我们new一个Promise的class并return给一个function

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!