【翻译】如何在React中使用async/await (componentDidMount Async)

最近更新:瓦伦提诺.加格利亚提 2018年4月30号 原文连接javascript

如何在React中使用async/await

pic1

你想像在NodeJS中使用async/await 那样在React中一样使用它们?html

create-react-app 构建的项目支持开箱即用。前端

可是若是你想在本身搭建的webpack配置的项目中使用,你可能会遇到 regeneratorRuntime is not defined 的异常错误。java

若是你遇到了这个错误还想在React中使用async/await,白日作梦。react

在此,咱们将循序渐进的实现:webpack

  • 修复 regeneratorRuntime is not defined 异常报错
  • 结合Fetch在React中使用async/await
  • 结合Fetch使用async/await处理异常

准备好了吗?git

(如下均是原文连接,紧接着会有翻译)github

  1. 什么是async/await
  2. 一个Promise的例子
  3. 使用async/await语法规则
  4. 处理异常
  5. 封装
  6. FAQ

什么是async/await

首先咱们先简单介绍一下async/await.web

async/await 只是JS中Promise的语法糖而已。npm

啥?JS里的Promise还不够你折腾?

Promise当然很好,可是某些状况下你会以长长的then/catch链了结。

我想说的是,若是你发觉本身在这样的处境内也很好,起码你简化了代码。

可是,async/await 能够以一种方式帮助你像编写同步代码那样的编写异步代码。

这会使得你的代码更加整洁和可读,且你还能够使用try/catch去合理的处理异常。

async/await 既方便又整洁:在某个种层面上你会想要把它用到你的React组件内。

让咱们看看如何实现。

一个Promise的例子

从克隆个人webpack仓库开始(这是一个基于webpack V4 的包含了开箱即用的React的快速开始项目):

git clone git@github.com:valentinogagliardi/webpack-4-quickstart.git

进入到文件夹内,安装依赖:

cd webpack-4-quickstart/ && npm i

用你最喜欢的编辑器打开工程目录,而后清空App.js里的内容。

接下来让咱们享受Promise吧。

假设你想经过fetch从API层获取数据。

这是很标准的的React流程。

你把API调用放到组件的componentDidMount方法里,代码以下:

// FILE: App.js
import React, { Component } from "react";
import ReactDOM from "react-dom";

class App extends Component {
  constructor() {
    super();
    this.state = { data: [] };
  }

  componentDidMount() {
    fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`)
      .then(res => res.json())
      .then(json => this.setState({ data: json }));
  }

  render() {
    return (
      <div> <ul> {this.state.data.map(el => ( <li> {el.name}: {el.price_usd} </li> ))} </ul> </div>
    );
  }
}

export default App;

ReactDOM.render(<App />, document.getElementById("app")); 复制代码

(上面这个组件只是一个示例:没有进行异常捕获处理,让咱们假设,咱们的fetch调用一路顺风,没有出现任何错误。)

若是你经过webpack-dev-server运行须要执行:

npm start

你会看到代码效果如期而至(虽然很丑,可是起码运行正常):

pic2

毫无想象力可言对吧?

咱们能够作的更好吗?在componentDidMount方法上使用async/await真的会更好吗?

让咱们试试看!

使用async/await语法规则

从7.6.0版本开始Node就普遍支持async/await语法了。

在前端则是另外一番景象。async/await并不能全面覆盖全部的浏览器。

pic3

(我知道,谁他娘的在意IE?)

总而言之,在React中使用async/await 一点也不神奇。

咱们应该在React的组件的哪里使用async/await呢?

像获取网络数据或者初始化事务放在React的componentDidMount方法里同样,把async/await放到这里是一个不错选择。

如下是几个在React里使用async/await的步骤:

  1. 把async关键字放到你的函数前
  2. 在函数体内使用await关键字
  3. catch捕获异常

还有一件事就是:async/await 并非支持全部的浏览器,这个细节你必须注意。

create-react-app支持开箱即用的async/await。

可是若是你想在本身的配置的webpack模板工程内使用,你会遇到一个错误(咱们立刻就会提到)。

如今让咱们在React组件内运用async/await吧。

打开App.js文件而后修改componentDidMount函数:

// FILE: App.js
import React, { Component } from "react";
import ReactDOM from "react-dom";

class App extends Component {
  constructor() {
    super();
    this.state = { data: [] };
  }

  async componentDidMount() {
    const response = await fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`);
    const json = await response.json();
    this.setState({ data: json });
  }

  render() {
    return (
      <div> <ul> {this.state.data.map(el => ( <li> {el.name}: {el.price_usd} </li> ))} </ul> </div>
    );
  }
}

export default App;

ReactDOM.render(<App />, document.getElementById("app")); 复制代码

没有捕获到异常,再一次的让咱们假设咱们的fetch调用平安无事。

在浏览器的console界面内瞅一眼。

工做正常吗?

pic4

regeneratorRuntime is not defined? 这是啥?

为了使用async/await,你该如何解决这个报错?

简单!

解决这个报错的关键就是babel-preset-env

在个人webpack 快速入门项目里包含着这个配置,若是你用的是本身已有的webpack配置模板,请确保你作了以下安装:

npm i babel-preset-env --save-dev

打开.babelrc文件,作以下内容更新:

{
    "presets": [
      ["env", {
        "targets": {
          "browsers": [
            ">0.25%",
            "not ie 11",
            "not op_mini all"
          ]
        }
      }], "react"
    ]
}
复制代码

能够查看Jamie Kyle’last 2 wersions harmful学习更多。

保存文件而后瞅一眼浏览器。

搞定了!

pic5

整洁而优雅,可是咱们革命还没有完成!

异常该怎么处理呢?

若是用户们掉线了或者API宕机了怎么办?

下个章节咱们就会讨论如何使用fetch和async/await处理异常

处理异常

咱们看过不少不进行异常处理的示例:

async componentDidMount() {
  const response = await fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`);
  const json = await response.json();
  this.setState({ data: json });
}
复制代码

我认可,在真正的app内,你会把fetch请求从视图层解耦出来。

然而,fetch APIs 在处理异常方面有一些说明。

TJ Van Toll有一篇不错的的文章对此做说明介绍:Handling Failed HTTP Responses With fetch()

因此,咱们该如何让咱们的代码更可靠呢?

让咱们在组件里作个实验吧。

经过移除URL里的coinmarketcap引入一个异常:

async componentDidMount() {
  const response = await fetch(`https://api.com/v1/ticker/?limit=10`);
  const json = await response.json();
  this.setState({ data: json });
}
复制代码

运行代码,瞅一眼console,你会发现:

  • TypeError: NetworkError when attempting to fetch resource, (火狐浏览器)
  • Uncaught (in promise) TypeError: Failed to fetch, (谷歌浏览器)

这里有一个没能捕获的异常。

让咱们抓住它。

添加一个try/catch块:

async componentDidMount() {
  try {
    const response = await fetch(`https://api.com/v1/ticker/?limit=10`);
    const json = await response.json();
    this.setState({ data: json });
  } catch (error) {
    console.log(error);
  }
}
复制代码

而后再次运行代码。

你会看到日志打印以下。

首先就是:wrap fetch inside a try/catch block to handle network errors.

如今让咱们在尝试一下其余方式。

若是度过TJ Van Toll的那篇文章,对于下面这个示例你就不会感到震惊。

请看代码:

async componentDidMount() {
  try {
    const response = await fetch(`http://httpstat.us/500`);
  } catch (error) {
    console.log(error);
  }
}
复制代码

你在日志面板看到什么了吗?毛都没有,没有异常,如同寸草不生的不毛之地。

为啥?

坏消息就是:当出现网络异常状况的时候,Fetch只会返回一个Promise对象。 譬如:用户掉线,DNS域名解析异常。

对于404 或者500这样的返回,你不会看到任何异常。

也就意味着你必须本身检查处理response返回体

好消息就是:Fetch的正常返回体都携带一个属性字段(叫作“ok”)是布尔类型的,true或false取决于HTTP的返回结果。

在下面的案例中,你能够用下面的方式处理异常:

async componentDidMount() {
  try {
    const response = await fetch(`http://httpstat.us/500`);
    if (!response.ok) {
      throw Error(response.statusText);
    }
  } catch (error) {
    console.log(error);
  }
}
复制代码

如咱们设想的那样,没有任何异常信息打印。

这时候,你能够向用户展现一些错误信息或者其余的有意义的内容。

因此第二点就是:对于HTTP的异常状况,Fetch并不会返回一个Promise对象。 自行检查返回体的ok字段。

回到咱们的示例,完整代码以下:

async componentDidMount() {
  try {
    const response = await fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`);
    if (!response.ok) {
      throw Error(response.statusText);
    }
    const json = await response.json();
    this.setState({ data: json });
  } catch (error) {
    console.log(error);
  }
}
复制代码

这个版本的处理异常的方式提供一个可靠的起始点。

再次重申,在真正的开发场景中你势必会吧fetch请求从视图层解耦出来,但那又是另一回事了。

想要学习更多关于Promise rejection in Async Functions请点击How to Throw Errors From Async Functions in Javascript

封装

从7.6.0版本日后,Node就开始全面支持async/await语法了。

async/await 能让你的代码更整洁和可读。

语法很方便,你会想要在你的React组件内使用的。

create-react-app 支持开箱即用的async/await.

可是若是你是用本身配制的webpack模板,你会遇到regeneratorRuntime is not defined异常。

解决这个异常的的关键是?babel-preset-env 和一些简单的配置。

今后你就只能够在React世界中放飞async/await了。

用在哪呢?

用的最多的地方就是用来获取网络数据和初始化一些数据的生命周期函数componentDidMount里,这里是一个绝佳的位置使用async/await.

遵循如下步骤,你即可以在React中使用async/await:

  • 配置babel,指定目标浏览器
  • 在componentDidMount方法钱使用async关键字
  • 在componentDidMount函数体内使用await关键字
  • 肯定你有作异常处理

若是你在本身的代码里使用 Fetch API ,在处理异常的时候要小心一些警告。

而后你就准备好了!

FAQ

componentDidMount是使用async/await最为合理的地方。

毕竟当组件瓜子啊完毕以后你想尽快的获取数据。

个人一位学生指出,你不能在componentWillMount方法内使用async/await

那是正确的:你不能在componentWillMount方法内使用Promise。

无论怎么说,我会尽可能少去componentWillMount这个方法内作一些操做的:那是一个在React生命周期中逐渐消逝获得方法

其余一些常见问题就是当在React中使用了async/await以后,包体的大小。

在不远的过去,若是不使用babel polyfill,你是没法在浏览器端使用async/await的。

使用它的结果就是,包体体积庞大。

现在有了babel-preset-env和目标浏览器的配置,状况不可同日而语。

包体大小仍在可接受的范围以内。

谢阅!

在老司机开车以前,抓紧时间上车!

相关文章
相关标签/搜索