async js 返回值_Async/Await 7 个优于普通 Promise 的原因

本文探讨了Async/Await在JavaScript中的优势,包括简洁清晰的语法、错误处理、条件判断、中间值处理、错误堆栈和调试便利性。通过对比Promise的用法,阐述了Async/Await如何使异步代码更易读、更易维护。
摘要由CSDN通过智能技术生成

4c43a3cec3e04d91957f36c1dc36a2d2.png

Async/Await 7 个优于普通 Promise 的原因

23fb2d0f8869bd0490a8f1f694d9d45b.png

Async/Await 在 NodeJS 7.6 中引入,目前所有的现代浏览器中均能支持使用。相信自 2017 年以来,它已成为 JS 最大的补充。如果你不相信的话,那么有很多理由能向你说明为什么应该立即使用它而不是停滞不前。

Async/Await

Async/Await 简单介绍:

  • Async/Await 是一种编写异步代码的新方法。异步代码之前的替代方法是 callback 和 promises。
  • Async/Await 实际上是在 promises 之上构建的语法糖。它不能与普通回调和 node 回调一起使用。
  • Async/Await 和 promises 一样是非阻塞的。
  • Async/Await 使得异步代码的外观看起来更像同步代码。

语法

假设一个 getJSON 函数返回一个 promise,并且这个 promise resolve 一个 JSON 对象。我们只想调用 getJSON 并且打印出 JSON,然后返回 done

下面是使用 promises 实现方式:

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

makeRequest()

下面,让我们看看使用 async/await 是如何实现的:

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

makeRequest()

上门两种写法的不同之处:

  1. 我们的函数之前有关键字 async,关键字 await 只能在使用 async 定义的函数内部使用。任何 async 函数都会隐式的返回 promise,并且 promise resolve 的值是我们从函数中返回的值(上述例子里是 done
  2. 上面的例子说明我们不能在代码最顶层直接使用 await,因为它不在异步函数中。
// this will not work in top level
// await makeRequest()

// this will work
makeRequest().then((result) => {
  // do something
})
  1. await getJSON() 意味着 console.log j将会在 getJSON() 的 promise resolves 后调用并打印 getJOSN 返回值。

为什么 Async/Await 更好

  1. Concise and clean

相比于 promise 实例中的代码,使用 Async/Await 中我们节省了大量代码。我们不需要写 .then,然后创建一个匿名函数来处理响应,也不必为不需要使用的数剧命名。我们还可以避免代码嵌套代码。这些优点累加起来在实例中更加明显。

  1. Error handling

Async/Await 使我们使用相同的 construct 去处理同步和异步的错误成为可能,在下面的 promises 实例中,如果 JSON.parse 如果报错 try/catch 不会处理,因为它是发生在 promis 内部的。我们需要在 promise 上调用 .catch 方法来处理错误。

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 现在回处理错误了:

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

想象一下下面这样的代码,他获取一些数据并根据数据中的某些值决定是否返回或者还是获取等多信息。

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
      }
    })
}

只是看上面的代码是否会让你头疼~,很容易迷失在代码的嵌套、括号和 return 语句中,而这些只是需要将最后结果返回给主 promise 即可。

当使用 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    
  }
}
  1. Intermediate values

你可能发现你处于这样一种情况:调用一个 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,那么将很容易去简化我们的嵌套代码,若果你是那种无法忍受这种写法的人的话,你可以将 value1 和 value2 包装起来传入 Promise.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 放入一个数组中。

使用 acync/await,相同的逻辑将变得非常简单且直观。

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

想象一下一段代码,它在一个链中调用多个回调,而在链的某处,会抛出一个错误。

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

从 promise 链中返回的错误堆栈,我们无法定位错误发生的位置。然而,如果错误信息的堆栈来自 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、then 更好。

  1. Debugging

使用 async/await 最大的优势是调试起来非常方便。调试 promise 一直很痛苦,原因如下:

  • 不能在返回表达式的箭头函数中设置断点(无正文)

5437e945d2ac8892bcc4a6a5ab138f81.png
  • 如果在 .then 中设置断点并使用诸如 step-over 的调试快捷方式,调试器将不会移动到以一个 .then。

使用 async/await,我们不需要那么多箭头函数,并且可以完全像正常的同步函数那样逐步执行 await 调用。

870aae379b6c720708f474ef979cfa83.png
  1. You can await anything

await 可以用于同步和异步表达式。例如可以编写 await 5,它等于 Promise.resolve(5)。这看起来似乎没什么用,但是在编写不知道输入是同步还是异步的库或实用函数时,这其实是一个很大的优势。

假设你要记录应用中执行一些 API 所需要的时间,然后你决定为此创建一个通用函数,下面是 promise 写法:

const recordTime = (makeRequest) => {
  const timeStart = Date.now();
  makeRequest().then(() => { // throws error for sync functions (.then is not a function)
    const timeEnd = Date.now();
    console.log('time take:', timeEnd - timeStart);
  })
}

你知道所有 api 调用都将返回 promise,但是如果使用相同的函数记录同步函数中花费的时间会怎样?因为 sync 函数不会返回 promise,所有将会引发错误,因为同步函数中不会返回 promise,所以会报错。避免这种情况发生通常是将 makeRequest 包装在 promise.resolve中。

如果使用 async/await,则不必担心这些情况发生。

const recordTime = async (makeRequest) => {
  const timeStart = Date.now();
  await makeRequest(); // works for any sync or async function
  const timeEnd = Date.now();
  console.log('time take:', timeEnd - timeStart);
}

结束语

Async/Await 是过去几年中 Javascript 最具革命性的功能之一。它是我们意识到了代码的混乱,并提供了直观的改进方法。

886

9f2c1153c8fc8cb534713ec46e634b1e.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值