最近更新:瓦倫提諾.加格利亞提 2018年4月30號 原文連接javascript
你想像在NodeJS中使用async/await 那樣在React中一樣使用它們?html
create-react-app 構建的項目支持開箱即用。前端
可是若是你想在本身搭建的webpack配置的項目中使用,你可能會遇到 regeneratorRuntime is not defined 的異常錯誤。java
若是你遇到了這個錯誤還想在React中使用async/await,白日作夢。react
在此,咱們將循序漸進的實現:webpack
準備好了嗎?git
(如下均是原文連接,緊接着會有翻譯)github
首先咱們先簡單介紹一下async/await.web
async/await 只是JS中Promise的語法糖而已。npm
啥?JS裏的Promise還不夠你折騰?
Promise當然很好,可是某些狀況下你會以長長的then/catch鏈了結。
我想說的是,若是你發覺本身在這樣的處境內也很好,起碼你簡化了代碼。
可是,async/await 能夠以一種方式幫助你像編寫同步代碼那樣的編寫異步代碼。
這會使得你的代碼更加整潔和可讀,且你還能夠使用try/catch去合理的處理異常。
async/await 既方便又整潔:在某個種層面上你會想要把它用到你的React組件內。
讓咱們看看如何實現。
從克隆個人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
你會看到代碼效果如期而至(雖然很醜,可是起碼運行正常):
毫無想象力可言對吧?
咱們能夠作的更好嗎?在componentDidMount方法上使用async/await真的會更好嗎?
讓咱們試試看!
從7.6.0版本開始Node就普遍支持async/await語法了。
在前端則是另外一番景象。async/await並不能全面覆蓋全部的瀏覽器。
(我知道,誰他孃的在意IE?)
總而言之,在React中使用async/await 一點也不神奇。
咱們應該在React的組件的哪裏使用async/await呢?
像獲取網絡數據或者初始化事務放在React的componentDidMount方法裏同樣,把async/await放到這裏是一個不錯選擇。
如下是幾個在React裏使用async/await的步驟:
還有一件事就是: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界面內瞅一眼。
工做正常嗎?
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學習更多。
保存文件而後瞅一眼瀏覽器。
搞定了!
整潔而優雅,可是咱們革命還沒有完成!
異常該怎麼處理呢?
若是用戶們掉線了或者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,你會發現:
這裏有一個沒能捕獲的異常。
讓咱們抓住它。
添加一個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:
若是你在本身的代碼裏使用 Fetch API ,在處理異常的時候要小心一些警告。
而後你就準備好了!
componentDidMount是使用async/await最爲合理的地方。
畢竟當組件瓜子啊完畢以後你想盡快的獲取數據。
個人一位學生指出,你不能在componentWillMount方法內使用async/await。
那是正確的:你不能在componentWillMount方法內使用Promise。
無論怎麼說,我會儘可能少去componentWillMount這個方法內作一些操做的:那是一個在React生命週期中逐漸消逝獲得方法。
其餘一些常見問題就是當在React中使用了async/await以後,包體的大小。
在不遠的過去,若是不使用babel polyfill,你是沒法在瀏覽器端使用async/await的。
使用它的結果就是,包體體積龐大。
現在有了babel-preset-env和目標瀏覽器的配置,狀況不可同日而語。
包體大小仍在可接受的範圍以內。
謝閱!
在老司機開車以前,抓緊時間上車!