本文由云+社区发表
本篇文章,小编将和你们一块儿学习异步编程的将来——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 的实现方式是否是更直观更容易理解呢,让我几乎能用同步的方式编写异步代码。
此文已由做者受权腾讯云+社区发布