async function_掌握 Async/Await
摘要: 還不用Async/Await就OUT了。。
- 原文:掌握 Async/Await
- 作者:Jartto
Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。
前端工程師肯定都經(jīng)歷過 JS 回調(diào)鏈獄的痛苦過程,我們在使用 Promise 的時候總是不盡人意。這時候 Async/Await 應(yīng)運而生,它到底有什么魔力,我們來說道說道。
一、回顧 Promise
所謂 Promise,簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果。
1. 語法
new Promise(executor); new Promise(function(resolve, reject) { ... });2. 參數(shù)
帶有 resolve 、reject 兩個參數(shù)的一個函數(shù)。這個函數(shù)在創(chuàng)建 Promise 對象的時候會立即得到執(zhí)行(在 Promise 構(gòu)造函數(shù)返回 Promise 對象之前就會被執(zhí)行),并把成功回調(diào)函數(shù)(resolve)和失敗回調(diào)函數(shù)(reject)作為參數(shù)傳遞進來。調(diào)用成功回調(diào)函數(shù)(resolve)和失敗回調(diào)函數(shù)(reject)會分別觸發(fā) Promise 的成功或失敗。
這個函數(shù)通常被用來執(zhí)行一些異步操作,操作完成以后可以選擇調(diào)用成功回調(diào)函數(shù)(resolve)來觸發(fā) Promise 的成功狀態(tài),或者,在出現(xiàn)錯誤的時候調(diào)用失敗回調(diào)函數(shù)(reject)來觸發(fā) Promise 的失敗。
3. Promise.all
用 Promise.all 來執(zhí)行,all 接收一個數(shù)組參數(shù),里面的值最終都算返回 Promise 對象。這樣,三個異步操作的并行執(zhí)行的,等到它們都執(zhí)行完后才會進到 then 里面。
Promise.all([async1(), async2(), async3()]) .then(function(results){console.log(results); });all 會把所有異步操作的結(jié)果放進一個數(shù)組中傳給 then,就是上面的 results。
4. Promise.race
all 方法的效果實際上是「誰跑的慢,以誰為準執(zhí)行回調(diào)」,那么相對的就有另一個方法「誰跑的快,以誰為準執(zhí)行回調(diào)」,這就是 race方法:
Promise.race([requestImg(), timeout()]) .then(function(results){console.log(results); }) .catch(function(reason){console.log(reason); });上述代碼演示了 race 的基本用法,實現(xiàn)的功能是:請求圖片,如果請求成功就返回圖片,否則就調(diào)用超時函數(shù)。
更多資源,請查看:
- js promise
- 白話 promise
- promise 對象-阮一峰
- promise
二、Promise 為何不完美?
乍一看,Promise 還不錯,幫我們解決了回調(diào)鏈獄的問題。當然這只是簡單使用,碰到復雜的業(yè)務(wù)也有很雞肋的場景,比如:
1. 錯誤處理
在下面的 Promise 示例中,Try/Catch 不能處理 JSON.parse 的錯誤,因為它在 Promise中。我們需要使用 catch,這樣錯誤處理代碼非常冗余。并且,在我們的實際生產(chǎn)代碼會更加復雜。
const makeRequest = () => {try {getJSON().then(result => {// JSON.parse可能會出錯const data = JSON.parse(result)console.log(data)})// 取消注釋,處理異步代碼的錯誤// .catch((err) => {// console.log(err)// })} catch (err) {console.log(err)} }Async/Await 讓 Try/Catch 可以同時處理同步和異步錯誤。使用 Async/Await 的話,Catch能處理 JSON.parse 錯誤:
const makeRequest = async () => {try {// this parse may failconst data = JSON.parse(await getJSON())console.log(data)} catch (err) {console.log(err)} }Async/Await 最讓人舒服的一點是代碼看起來是同步的。
2. 條件語句
下面示例中,需要獲取數(shù)據(jù),然后根據(jù)返回數(shù)據(jù)決定是直接返回,還是繼續(xù)獲取更多的數(shù)據(jù)。
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層),括號,return 語句很容易讓人感到迷茫,而它們只是需要將最終結(jié)果傳遞到最外層的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; } }所以,這才是真正擺脫回調(diào)鏈獄的正確做法。
3. 中間值
你很可能遇到過這樣的場景,調(diào)用 promise1,使用 promise1 返回的結(jié)果去調(diào)用 promise2,然后使用兩者的結(jié)果去調(diào)用promise3。你的代碼很可能是這樣的:
const makeRequest = () => {return promise1().then(value1 => {return promise2(value1);.then(value2 => { return promise3(value1, value2);})}) } // 或者: const makeRequest = () => {return promise1().then(value1 => {return Promise.all([value1, promise2(value1)])}).then(([value1, value2]) => { return promise3(value1, value2)}) }怎么寫都會覺得很復雜,那如果 Async/Await 用來實現(xiàn)呢,表現(xiàn)可能如下:
const makeRequest = async () => {const value1 = await promise1();const value2 = await promise2(value1);return promise3(value1, value2); }是不是很 6 ,將復雜的場景簡化,這樣的代碼就很有靈性了。
4. 錯誤棧
調(diào)用了多個 Promise,假設(shè) Promise 鏈中某個地方拋出了一個錯誤,Promise 鏈中返回的錯誤棧沒有給出錯誤發(fā)生位置的線索。更糟糕的是,它會誤導我們;錯誤棧中唯一的函數(shù)名為 callAPromise,然而它和錯誤沒有關(guān)系。(文件名和行號還是有用的)。
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) })然而,Async/Await 中的錯誤棧會指向錯誤所在的函數(shù):
const makeRequest = async () => {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) })5. 調(diào)試
調(diào)試 Promise 有兩個問題:
- 不能在返回表達式的箭頭函數(shù)中設(shè)置斷點;
- 如果你在 then 代碼塊中設(shè)置斷點,調(diào)試器不會跳到下一個 then,因為它只會跳過異步代碼;
而使用 Await/Async 時,你不再需要那么多箭頭函數(shù),這樣你就可以像調(diào)試同步代碼一樣跳過 Await 語句。
這里只簡單的列出問題,詳細請查看原文:Async/Await 替代 Promise 的 6 個理由
三、新時代的曙光 Async/Await
簡單介紹:
- Await/Async 是寫異步代碼的新方式,以前的方法有回調(diào)函數(shù)和 Promise。
- Await/Async 是基于 Promise 實現(xiàn)的,它不能用于普通的回調(diào)函數(shù)。
- Await/Async 與 Promise 一樣,是非阻塞的。
- Await/Async 使得異步代碼看起來像同步代碼,這正是它的魔力所在。
使用 Promise 是這樣的:
const jarttoDemo = () => getJSON().then(data => {return data; })jarttoDemo();使用 Async/Await 是這樣的:
const jarttoDemo = async () => {let data = await getJSON();return data; }jarttoDemo();基本規(guī)則:
- Async 表示這是一個 Async 函數(shù),Await 只能用在這個函數(shù)里面。
- Await 表示在這里等待 Promise返回結(jié)果了,再繼續(xù)執(zhí)行。
- Await 后面跟著的應(yīng)該是一個 Promise 對象,當然,其他返回值也沒關(guān)系,只是會立即執(zhí)行,不過那樣就沒有意義了。
四、更多用法示例
1. 簡單示例
var sleep = function (time) {return new Promise(function (resolve, reject) {setTimeout(function () {resolve();}, time);}) };var start = async function () {// 在這里使用起來就像同步代碼那樣直觀console.log('start');await sleep(3000);console.log('end'); };start();2. 獲得返回值
var sleep = function (time) {return new Promise(function (resolve, reject) {setTimeout(function () {// 返回 ‘ok’resolve('ok');}, time);}) };var start = async function () {let result = await sleep(3000);console.log(result); // 收到 ‘ok’ };3. 錯誤捕獲
const makeRequest = async () => {try {// this parse may failconst data = JSON.parse(await getJSON())console.log(data)} catch (err) {console.log(err)} }既然 then 不用寫了,那么 catch 也不用寫,可以直接用標準的 try catch 語法捕捉錯誤。
var sleep = function (time) {return new Promise(function (resolve, reject) {setTimeout(function () {// 模擬出錯了,返回 ‘error’reject('error');}, time);}) };var start = async function () {try {console.log('start');await sleep(3000); // 這里得到了一個返回錯誤// 所以以下代碼不會被執(zhí)行了console.log('end');} catch (err) {console.log(err); // 這里捕捉到錯誤 `error`} };4. 條件語句
Promise 寫法:
const makeRequest = () => {return getJSON().then(data => {if (data.needsAnotherRequest) {return makeAnotherRequest(data).then(moreData => {return moreData;})} else {return data;}}) }Async/Await 寫法:
const makeRequest = async () => {const data = await getJSON();if (data.needsAnotherRequest) {const moreData = await makeAnotherRequest(data);return moreData;} else {return data; } }5. 循環(huán)多個 Await
var start = async function () {for (let i = 1; i <= 10; i++) {console.log(`當前是第 ${i} 次等待..`);await sleep(1000);} };需要注意的是,Await 必須在 Async 函數(shù)的上下文中的。
6. 在 forEach 中使用
async function printFiles() {const files = await getFilePaths();for (let file of files) {const contents = await fs.readFile(file, "utf8");console.log(contents);} }async function printFiles() {const files = await getFilePaths();await Promise.all(files.map(file => {return async () => {const contents = await fs.readFile(file, "utf8");console.log(contents);};})); }示例參考如下文章:
- Async/Await替代Promise的6個理由
- ES7 的 Async/Await
- Using async/await with a forEach loop
五、總結(jié)
我們一直在強調(diào)代碼的可讀性和可維護性,對我來說,Async/Await 更加易懂和易用。所以,不管是 Promise 還是 Async/Await ,能解決實際問題的技術(shù)就是好技術(shù)。
當然,Async/Await 也是基于 Promise 概念的,技術(shù)上我們也可以求同存異,不必太過較真。一句話,選擇權(quán)在你!
總結(jié)
以上是生活随笔為你收集整理的async function_掌握 Async/Await的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电子产品设计流程_产品设计“学习、就业、
- 下一篇: ue4 怎么修改骨骼动画_UE4换装系统