Published on

异步编程

Authors

在 JavaScript 中,存在一些异步操作,需要等浏览器执行完成之后,再告诉主线程,我们再去做后续的操作 之前为了实现这一点,我们会用回调函数的方式,拿到异步的执行结果,并做后续处理

但如果我们在某一个回调函数中,又依赖其他回调函数带过来的参数,就会存在回调地狱(混乱的嵌套),且难以维护和控制状态,由此 es6 引入了 Promise 的概念。一个 Promise 本身只有三种状态,pending(待定),fulfilled(成功),rejected(失败),当状态从待定,变成成功或失败后,状态将固定不再发生变化。它支持链式调用,then 和 catch 方法总会返回一个新的 promise 对象,从而将嵌套的回调拉平,且可以将状态通过链式传递下去。

如果then中的回调函数:

  • 返回了一个值(包括另一个Promise),那么新返回的Promise会以这个值作为成功状态的结果。
  • 没有返回值,新返回的Promise会以undefined作为成功状态的结果。
  • 抛出了错误,新返回的Promise则会变为失败状态。

而 Async 和 Await,则是Promise 的语法糖,通过它,我们可以使用同步的代码写法,实现异步的功能。一个函数被async关键字修饰后,​它无论如何都会返回一个Promise对象。

  • 如果函数内显式返回一个非Promise值,这个值会被自动包装成一个已解决的Promise;
  • 如果函数抛出异常,则会返回一个被拒绝的Promise。

如何使用它?

new Promise((resolve, reject) => {
  setTimeout(() => {
    // 模拟一个异步接口请求
    resolve(true)
  }, 1000)
})
  .then((data) => {
    console.log(data)
  })
  .catch((error) => {
    console.error(error)
  })

const getData = async () => {
  try {
    const response = await fetch('https://www.api.com')
    console.log('response', response)
  } catch (e) {
    console.error('e', e)
  }
}

解决了什么问题? Promise 解决了回调地狱,便于管理状态。Async/Await 让异步处理可以跟同步一样,使代码逻辑更清晰

最佳实践有哪些?

  1. 无依赖的异步任务使用 Promise.all并行执行提升效率
  2. 手动处理接口超时,可以定义一个超时Promise(比如限定 30s 后,返回失败),使用 Promise.race([]),将实际接口请求,和超时 Promise 都放进去,让它们"竞速"
  3. 在使用 async/await 的时候,一定要用 try/catch 做错误处理
  4. Promise 链中必须使用 catch 做错误处理