大家好,上周我们一起学习了《JavaScript基础——Promise使用指南》, 明白了ES6增加的新特性——Promise让我们能够更加优雅的书写回调函数,清楚了Promise有哪些状态,以及如何编写Promise的相关代码。本篇文章,小编将和大家一起学习异步编程的未来——async/await,它会打破你对上篇文章Promise的认知,竟然异步代码还能这么写! 但是别太得意,你需要深入理解Promise后,才能更好的的驾驭async/await,因为async/await是基于Promise的,没有理解Promise,小编强烈建议各位再看看《JavaScript基础——Promise使用指南》。
让我们从async关键字开始吧,这个关键字可以放在函数之前,如下所示:
async function f() {
return 1;
}
在函数之间加上async意味着:函数将返回一个Promise,虽然你的代码里没有显示的声明返回一个Promise,但是编译器会自动将其转换成一个Promise,不信你可以使用Promise的then方法试试:
async function f() {
return 1;
}
f().then(alert); // 1
...如果你不放心的话,你可以在代码里明确返回一个Promise,输出结果是相同的。
async function f() {
return Promise.resolve(1);
}
f().then(alert); // 1
很简单吧,小编之所以说 async/await 是基于Promise是没毛病的,async函数返回一个Promise,很简单吧,不仅如此,还有一个关键字await,await只能在async中运行。
await的基本语法:
let value=await promise;
该关键字的await的意思就是让JS编译器等待Promise并返回结果。接下来我们看一段简单的示例:
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”的提示框。
小编在此强调下:
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 的实现方式是不是更直观更容易理解呢,让我几乎能用同步的方式编写异步代码。
本节的内容就介绍到这里,我们学会了如何使用 async/await,并且学会了如何运用Promise相关的API进行结合,async/await 让我们以同步的方式编写异步代码,大大降低了编写异步函数的难度。接下来,小编将介绍什么是JavaScript的作用域和编译原理,敬请期待。
更多精彩内容,请微信关注”前端达人”公众号!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。