资讯专栏INFORMATION COLUMN

JavaScript:体验异步的优雅解决方案

happyfish / 2048人阅读

摘要:但是的的出现碉堡的新朋友,我们可以轻松写出同步风格的代码同时又拥有异步机制,可以说是目前最简单,最优雅,最佳的解决方案了。不敢说这一定是终极的解决方案,但确实是目前最优雅的解决方案

一、异步解决方案的进化史

JavaScript的异步操作一直是个麻烦事,所以不断有人提出它的各种解决方案。可以追溯到最早的回调函数(ajax老朋友),到Promise(不算新的朋友),再到ES6的Generator(强劲的朋友)。
几年前我们可能用过一个比较著名的Async.js,但是它没有摆脱回调函数,并且错误处理也是按照“回调函数的第一个参数用来传递错误”这样一个约定。而众所周知的回调地狱仍然是一个比较突出的问题,直到Generator改变了这种异步风格。
但是ES7的async await的出现(碉堡的新朋友),我们可以轻松写出同步风格的代码同时又拥有异步机制,可以说是目前最简单,最优雅,最佳的解决方案了。

二、async await语法

async await语法比较简单,可以认为是Generator的语法糖,比起星号和yield更具有语义化。下面一个简单的例子表示1秒之后输出hello world:

</>复制代码

  1. function timeout(ms) {
  2. return new Promise((resolve) => {
  3. setTimeout(resolve, ms);
  4. });
  5. }
  6. async function asyncPrint(value, ms) {
  7. await timeout(ms);
  8. console.log(value)
  9. }
  10. asyncPrint("hello world", 1000);

await只能用在async函数中,如果用在普通函数就会报错

await后面跟的是一个Promise对象(当然其它值也可以,但是会包装成一个立即resolve的Promise,也就没有意义了)

await会等待Promise的结果返回再继续执行

await等待的虽然是Promise对象,但是不必写.then(),直接可以得到返回值,将上面的代码微调,发现返回值result也是可以输出hello world:

</>复制代码

  1. function timeout(ms) {
  2. return new Promise((resolve) => {
  3. setTimeout(_ => {resolve("hello world")}, ms);
  4. });
  5. }
  6. async function asyncPrint(ms) {
  7. let result = await timeout(ms);
  8. console.log(result)
  9. }
  10. asyncPrint(1000);
三、async await错误处理

前面说了await等待的虽然是Promise对象,但是不必写.then(),所以其实也不用写.catch()了,直接用try catch就能捕捉错误,这样可以避免错误处理代码非常冗余和笨重,还是将上面的例子微调:

</>复制代码

  1. function timeout(ms) {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(_ => {reject("error")}, ms);//reject模拟出错,返回error
  4. });
  5. }
  6. async function asyncPrint(ms) {
  7. try {
  8. console.log("start");
  9. await timeout(ms);//这里返回了错误
  10. console.log("end");//所以这句代码不会被执行了
  11. } catch(err) {
  12. console.log(err); //这里捕捉到错误error
  13. }
  14. }
  15. asyncPrint(1000);

如果有多个await,可以一起放在try catch中:

</>复制代码

  1. async function main() {
  2. try {
  3. const async1 = await firstAsync();
  4. const async2 = await secondAsync();
  5. const async3 = await thirdAsync();
  6. }
  7. catch (err) {
  8. console.error(err);
  9. }
  10. }
四、async await注意点

1). 前面已经说过,await命令后面的Promise对象,运行结果很可能是reject或逻辑报错,所以最好把await放在try catch代码块中。
2). 多个await命令的异步操作,如果不存在依赖关系,让它们同时触发。

</>复制代码

  1. const async1 = await firstAsync();
  2. const async2 = await secondAsync();

上面代码中,async1和async2如果是两个独立的异步操作,这样写会比较耗时,因为只有firstAsync完成以后,才会执行secondAsync,完全可以用Promise.all优雅地处理:

</>复制代码

  1. let [async1, async2] = await Promise.all([firstAsync(), secondAsync()]);

3). await只能用在async函数之中,如果用在普通函数就会报错:

</>复制代码

  1. async function main() {
  2. let docs = [{}, {}, {}];
  3. //报错 await is only valid in async function
  4. docs.forEach(function (doc) {
  5. await post(doc);
  6. console.log("main");
  7. });
  8. }
  9. function post(){
  10. return new Promise((resolve) => {
  11. setTimeout(resolve, 1000);
  12. });
  13. }

在forEach内部方法加上async就可以了:

</>复制代码

  1. async function main() {
  2. let docs = [{}, {}, {}];
  3. docs.forEach(async function (doc) {
  4. await post(doc);
  5. console.log("main");
  6. });
  7. }
  8. function post(){
  9. return new Promise((resolve) => {
  10. setTimeout(resolve, 1000);
  11. });
  12. }

但是你会发现3个main是同时输出的,这就说明post是并发执行的,而不是继发执行,改成for就可以解决问题,3个main是分别相隔1秒输出:

</>复制代码

  1. async function main() {
  2. let docs = [{}, {}, {}];
  3. for (let doc of docs) {
  4. await post(doc);
  5. console.log("main");
  6. }
  7. }
  8. function post(){
  9. return new Promise((resolve) => {
  10. setTimeout(resolve, 1000);
  11. });
  12. }

总之,用了async await之后整个人神清气爽,可以用非常简洁和优雅的代码实现各种花式异步操作,并且在业务逻辑复杂的情况下可以不用陷入回调地狱中。不敢说这一定是终极的解决方案,但确实是目前最优雅的解决方案!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90736.html

相关文章

  • JavaScript优雅实现顺序执行异步函数

    摘要:顺序执行异步函数异步为带来非阻塞等优势的同时,同时也在一些场景下带了不便,如顺序执行异步函数,下面总结了一些常用的方法。 火于异步 1995年,当时最流行的浏览器——网景中开始运行 JavaScript (最初称为 LiveScript)。 1996年,微软发布了 JScript 兼容 JavaScript。随着网景、微软竞争而不断的技术更新,在 2000年前后,JavaScript ...

    monw3c 评论0 收藏0
  • JS笔记

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。异步编程入门的全称是前端经典面试题从输入到页面加载发生了什么这是一篇开发的科普类文章,涉及到优化等多个方面。 TypeScript 入门教程 从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 网络基础知识之 HTTP 协议 详细介绍 HTT...

    rottengeek 评论0 收藏0
  • JavaScript 异步

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。写一个符合规范并可配合使用的写一个符合规范并可配合使用的理解的工作原理采用回调函数来处理异步编程。 JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过...

    tuniutech 评论0 收藏0
  • async 更优雅异步体验

    摘要:文章同步自个人博客上一篇让自启动介绍了通过起动器让跑起来,而本篇采用实现更优雅的异步编程。而采用写,代码则是直接运行即可直接运行了,无须写生成器来运行了,而代码仅仅是改为改为而已。不过效果确实非常好,让异步编程更加的同步了。 文章同步自个人博客:http://www.52cik.com/2016/07/11/generator-co.html 上一篇《让 Generator 自启动》介...

    Ajian 评论0 收藏0
  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<