本文由雲+社區發表
本篇文章,小編將和你們一塊兒學習異步編程的將來——async/await,它會打破你對上篇文章Promise的認知,居然異步代碼還能這麼寫! 可是別太得意,你須要深刻理解Promise後,才能更好的的駕馭async/await,由於async/await是基於Promise的。javascript
讓咱們從async關鍵字開始吧,這個關鍵字能夠放在函數以前,以下所示:html
async function f() { return 1; }
在函數之間加上async意味着:函數將返回一個Promise,雖然你的代碼裏沒有顯示的聲明返回一個Promise,可是編譯器會自動將其轉換成一個Promise,不信你可使用Promise的then方法試試:java
async function f() { return 1; } f().then(alert); // 1
...若是你不放心的話,你能夠在代碼裏明確返回一個Promise,輸出結果是相同的。web
async function f() { return Promise.resolve(1); } f().then(alert); // 1
很簡單吧,小編之因此說 async/await 是基於Promise是沒毛病的,async函數返回一個Promise,很簡單吧,不只如此,還有一個關鍵字await,await只能在async中運行。npm
await的基本語法:編程
let value=await promise;
該關鍵字的await的意思就是讓JS編譯器等待Promise並返回結果。接下來咱們看一段簡單的示例:json
async function f() { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("done!"), 1000) }); let result = await promise; // wait till the promise resolves (*) alert(result); // "done!" } f();
函數執行將會在 let result = await promise 這一行暫停,直到Promise返回結果,所以上述代碼將會1秒後,在瀏覽器彈出「done」的提示框。數組
小編在此強調下:promise
function f() { let promise = Promise.resolve(1); let result = await promise; // Syntax error }
一塊兒動手以前,確保你安裝了Node,NPM相關工具,谷歌瀏覽器,爲了預覽代碼效果,小編使用 npm install http-server -g 命令快速部署了web服務環境,方便咱們運行代碼。接下來,咱們寫一個火箭發射場景的小例子(不是真的發射火箭😆)。瀏覽器
async function getRandomNumber() { console.log('Getting random number.'); return Math.random(); }
async function deteremineReadyToLaunch(percentage) { console.log('Determining Ready to launch.'); return percentage>0.5; }
async function reportResults(isReadyToLaunch) { if (isReadyToLaunch) { console.log('Rocket ready to launch. Initiate countdown: 🚀'); } else { console.error('Rocket not ready. Abort mission: '); } }
export function main() { console.log('Before Promise created'); getRandomNumber() .then(deteremineReadyToLaunch) .then(reportResults) console.log('After Promise created'); }
<html> <script type="module"> import {main} from './main.js'; main(); </script> <body> </body> </html>
上一小節,咱們使用Promise.then依次處理了多個結果,本小節,小編將使用await實現一樣的功能,具體操做以下:
async function getRandomNumber() { console.log('Getting random number.'); return Math.random(); }
async function deteremineReadyToLaunch(percentage) { console.log('Determining Ready to launch.'); return percentage>0.5; }
async function reportResults(isReadyToLaunch) { if (isReadyToLaunch) { console.log('Rocket ready to launch. Initiate countdown: 🚀'); } else { console.error('Rocket not ready. Abort mission: '); } }
export async function main() { const randomNumber = await getRandomNumber(); const ready = await deteremineReadyToLaunch(randomNumber); await reportResults(ready); }
有時候咱們須要同時啓動多個異步,無需依次等待結果消耗時間,接下來的例子可使用await同時啓動多個異步函數,等待多個結果。
function checkEngines() { console.log('checking engine'); return new Promise(function (resolve) { setTimeout(function() { console.log('engine check completed'); resolve(Math.random() < 0.9) }, 250) }); } function checkFlightPlan() { console.log('checking flight plan'); return new Promise(function (resolve) { setTimeout(function() { console.log('flight plan check completed'); resolve(Math.random() < 0.9) }, 350) }); } function checkNavigationSystem() { console.log('checking navigation system'); return new Promise(function (resolve) { setTimeout(function() { console.log('navigation system check completed'); resolve(Math.random() < 0.9) }, 450) }); }
export async function main() { const enginePromise = checkEngines(); const flighPlanPromise = checkFlightPlan(); const navSystemPromise = checkNavigationSystem(); const enginesOk = await enginePromise; const flighPlanOk = await flighPlanPromise; const navigationOk = await navSystemPromise; if (enginesOk && flighPlanOk && navigationOk) { console.log('All systems go, ready to launch: 🚀'); } else { console.error('Abort the launch: '); if (!enginesOk) { console.error('engines not ready'); } if (!flighPlanOk) { console.error('error found in flight plan'); } if (!navigationOk) { console.error('error found in navigation systems'); } } }
在上一小節中,咱們一塊兒學習瞭如何觸發多個異步函數並等待多個異步函數結果。上一節咱們只使用了asyc/await,本節小編和你們一塊兒使用Promise.all來收集多個異步函數的結果,在某些狀況下,儘可能使用Promise相關的API,具體的代碼以下:
建立三個函數功能checkEngines,checkFlightPlan,和checkNavigationSystem用來記錄信息時,這三個函數都返回一個Promise,示例代碼以下:
function checkEngines() { console.log('checking engine'); return new Promise(function (resolve) { setTimeout(function() { console.log('engine check completed'); resolve(Math.random() < 0.9) }, 250) }); } function checkFlightPlan() { console.log('checking flight plan'); return new Promise(function (resolve) { setTimeout(function() { console.log('flight plan check completed'); resolve(Math.random() < 0.9) }, 350) }); } function checkNavigationSystem() { console.log('checking navigation system'); return new Promise(function (resolve) { setTimeout(function() { console.log('navigation system check completed'); resolve(Math.random() < 0.9) }, 450) }); }
export async function main() { const prelaunchChecks = [ checkEngines(), checkFlightPlan(), checkNavigationSystem() ]; const checkResults = await Promise.all(prelaunchChecks); const readyToLaunch = checkResults.reduce((acc, curr) => acc && curr); if (readyToLaunch) { console.log('All systems go, ready to launch: 🚀'); } else { console.error('Something went wrong, abort the launch: '); } } }
Promise.all接收多個promise的數組,並總體返回一個Promise,若是和上一小節的代碼進行比較,代碼量少了很多。
並不是全部的async都能成功返回,咱們須要處理程序的異常,在本小節中,你將會看到如何使用try-catch捕獲async函數引起的異常,具體操做的流程以下:
async function addBoosters() { throw new Error('Unable to add Boosters'); }
async function performGuidanceDiagnostic (rocket) { throw new Error('Unable to finish guidance diagnostic')); }
export async function main() { console.log('Before Check'); try { await addBosters(); await performGuidanceDiagnostic(); } catch (e) { console.error(e); } } console.log('After Check');
從輸出看出,咱們使用熟悉的try-catch捕獲到了異常,若是第一個發生異常,第二個就不會執行,同時將會被記錄到,並輸出到控制檯,在下一小節,咱們將一塊兒學習如何使用try-catch捕獲Promise.all中運行的多個Promise的異常。
在上一小節,咱們使用了Promise.all來收集多個異步函數的結果。在收集異常方面,Promise.all更有趣。一般,咱們在處理多個錯誤時,同時顯示多個錯誤信息,咱們必須編寫相關的業務邏輯。可是,在這小節,你將會使用Promise.all和try-catch捕獲異常,無需編寫複雜的布爾邏輯處理業務,具體如何實現示例以下:
function checkEngines() { console.log('checking engine'); return new Promise(function (resolve, reject) { setTimeout(function () { if (Math.random() > 0.5) { reject(new Error('Engine check failed')); } else { console.log('Engine check completed'); resolve(); } }, 250) }); } function checkFlightPlan() { console.log('checking flight plan'); return new Promise(function (resolve, reject) { setTimeout(function () { if (Math.random() > 0.5) { reject(new Error('Flight plan check failed')); } else { console.log('Flight plan check completed'); resolve(); } }, 350) }); } function checkNavigationSystem() { console.log('checking navigation system'); return new Promise(function (resolve, reject) { setTimeout(function () { if (Math.random() > 0.5) { reject(new Error('Navigation system check failed')); } else { console.log('Navigation system check completed'); resolve(); } }, 450) }); }
建立一個async的main函數調用每一個在上一步中建立的功能函數。等待結果,捕獲並記錄引起的異常。若是沒有拋出異常,則記錄成功:
export async function main() { try { const prelaunchChecks = [ checkEngines, checkFlightPlan, checkNavigationSystem ]; await Promise.all(prelauchCheck.map((check) => check()); console.log('All systems go, ready to launch: 🚀'); } catch (e) { console.error('Aborting launch: '); console.error(e); } } }
Promise.all返回一個Promise,當await在錯誤狀態下,會拋出異常。三個異步promise同時執行,若是其中一個或多個錯誤獲得知足,則會拋出一個或多個錯誤;
你會發現只有一個錯誤會被記錄下來,與同步代碼同樣,咱們的代碼可能會拋出多個異常,但只有一個異常會被catch捕獲並記錄。
錯誤處理可能會變得至關複雜。有些狀況,其中你但願發生錯誤時繼續冒泡調用堆棧以便執行其它更高級別處理。在這些狀況下,您可能還須要執行一些清理任務。本小節,你將瞭解如何使用finally以確保執行某些代碼,而無論錯誤狀態如何,具體如何實現示例以下:
function checkEngines() { console.log('checking engine'); return new Promise(function (resolve, reject) { setTimeout(function () { if (Math.random() > 0.5) { reject(new Error('Engine check failed')); } else { console.log('Engine check completed'); resolve(); } }, 250) }); } function checkFlightPlan() { console.log('checking flight plan'); return new Promise(function (resolve, reject) { setTimeout(function () { if (Math.random() > 0.5) { reject(new Error('Flight plan check failed')); } else { console.log('Flight plan check completed'); resolve(); } }, 350) }); } function checkNavigationSystem() { console.log('checking navigation system'); return new Promise(function (resolve, reject) { setTimeout(function () { if (Math.random() > 0.5) { reject(new Error('Navigation system check failed')); } else { console.log('Navigation system check completed'); resolve(); } }, 450) }); }
async function performChecks() { console.log('Starting Pre-Launch Checks'); try { const prelaunchChecks = [ checkEngines, checkFlightPlan, checkNavigationSystem ]; return Promise.all(prelauchCheck.map((check) => check()); } finally { console.log('Completed Pre-Launch Checks'); } }
export async function main() { try { await performChecks(); console.log('All systems go, ready to launch: 🚀'); } catch (e) { console.error('Aborting launch: '); console.error(e); } }
與上一小節同樣,異常在main函數中進行捕獲,因爲finally的存在,讓我清楚的知道performChecks確保須要執行的輸出完成。你能夠設想,處理錯誤是一個重要的任務,而且async/await容許咱們使用try/catch的方式同時處理異步和同步代碼的錯誤,大大簡化了咱們處理錯誤的工做量,讓代碼更加簡潔。
上篇文章《JavaScript基礎——Promise使用指南》的最後,咱們使用Promise的方法改寫了回調的例子,本文的最後,咱們將用今天學到的內容async/await改寫這個例子,如何實現呢,代碼以下:
const fs = require('fs'); const path = require('path'); const postsUrl = path.join(__dirname, 'db/posts.json'); const commentsUrl = path.join(__dirname, 'db/comments.json'); //return the data from our file function loadCollection(url) { return new Promise(function(resolve, reject) { fs.readFile(url, 'utf8', function(error, data) { if (error) { reject('error'); } else { resolve(JSON.parse(data)); } }); }); } //return an object by id function getRecord(collection, id) { return new Promise(function(resolve, reject) { const data = collection.find(function(element){ return element.id == id; }); resolve(data); }); } //return an array of comments for a post function getCommentsByPost(comments, postId) { return comments.filter(function(comment){ return comment.postId == postId; }); } async function getPost(){ try { const posts = await loadCollection(postsUrl); const post = await getRecord(posts, "001"); const comments = await loadCollection(commentsUrl); const postComments = await getCommentsByPost(comments, post.id); console.log(post); console.log(postComments); } catch (error) { console.log(error); } } getPost();
和Promise的方式相比,async/await 的實現方式是否是更直觀更容易理解呢,讓我幾乎能用同步的方式編寫異步代碼。
此文已由做者受權騰訊雲+社區發佈