ES6 Async与异步编程

更新日期: 2019-05-15 阅读: 2.1k 标签: 编程

单线程是Javascript语言最本质的特性之一,Javascript引擎在运行js代码的时候,同一个时间只能执行单个任务。这种模式的好处是实现起来比较简单,执行环境相对单纯。

坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。所以异步编程对JavaScript语言太重要。有些小伙伴可能还不太理解"异步"。

所谓的"异步",就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

例如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步。

相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。

讲的通俗点:

朱自清的《背影》中,父亲对朱自清说 :“我买几个橘子去。你就在此地,不要走动。”,朱自清没有走动,等着买完橘子的父亲一起吃橘子,就叫同步。如果朱自清没有等父亲,独自走了,那就不能和父亲一起吃橘子,就叫异步。


1、异步编程

我们就以用户注册这个特别常见的场景为例,讲讲异步编程。

第一步,验证用户是否注册
第二步,没有注册,发送验证码
第三步,填写验证码、密码,检验验证码是否正确

这个过程是有一定的顺序的,你必须保证上一步完成,才能顺利进行下一步。


1.1 回调函数

function testRegister(){}  // 验证用户是否注册
function sendMessage(){}   // 给手机发送验证码x
function testMessage(){}   // 检验验证码是否正确

function doRegister(){  //开始注册
    testRegister(data){
        if(data===false){ //已注册
            
        }else{ //未注册
             sendMessage(data){
                 if(data===true){ //发送验证码成功
                    testMessage(data){
                        if(data===true){  //验证码正确
                           
                        }else{  //验证码不正确
                            
                        }
                    }    
                }
            }
        }
    }
}

代码中就已经有许多问题,比如杂乱的 if 判断语句 、层层嵌套的函数,造成代码的可读性差,难于维护。

另外,如果在层层回调函数中出现异常,调试起来是非常让人奔溃的 —— 由于 try-catch 无法捕获异步的异常,我们只能不断不断的写 debugger 去追踪,简直步步惊心。

这种层层嵌套被称为回调地狱。


1.2 Promise方式

Promise就是为了解决回调地狱问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。采用Promise,连续读取多个文件,写法如下。

let state=1;  //模拟返回结果
function step1(resolve,reject){
    console.log('1. 验证用户是否注册');
    if(state==1){
        resolve('未注册');
    }else{
        reject('已注册');
    }
}
function step2(resolve,reject){
    console.log('2.给手机发送验证码');
    if(state==1){
        resolve('发送成功');
    }else{
        reject('发送失败');
    }
}
function step3(resolve,reject){
    console.log('3.检验验证码是否正确');
     if(state==1){
        resolve('验证码正确');
    }else{
        reject('验证码不正确');
    }
}

new Promise(testRegister).then(function(val){ // 验证用户是否注册
    console.log(val);
    return new Promise(sendMessage);   // 给手机发送验证码
}).then(function(val){
     console.log(val);
    return new Promise(testMessage);  // 检验验证码是否正确
}).then(function(val){
    console.log(val);
    return val;
});

回调函数采用了嵌套的方式依次调用testRegister()、sendMessage() 和testMessage(),而Promise使用then将它们链接起来。相比回调函数而言,Promise代码可读性更高,代码的执行顺序一目了然。

Promise的方式虽然解决了回调地狱,但是最大问题是代码冗余,原来的任务被Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。代码流程不能很好的表示执行流程。

大家初中学过电路,这个就像电路的串联,如果没学过也没关系,你肯定知道jquery有链式操作,这个就很类似链式操作的写法,比较符合我们的思维逻辑。


1.3 async/await方式

async语法是对new Promise的包装,await语法是对then方法的提炼。

 async function doRegister(url) {
  let data  = await testRegister();     // 验证用户是否注册
  let data2 = await sendMessage(data);  // 给手机发送验证码
  let data3 = await testMessage(data2); // 检验验证码是否正确
  return data3
}

上面的代码虽然短,但是每一句都极为重要。data 是 await testRegister的返回结果,data2 又使用了 data 作为sendMessage的参数,data3 又使用了data2 作为testMessage的参数。

只要在doRegister前面加上关键词async,在函数内的异步任务前添加await声明即可。如果忽略这些额外的关键字,简直就是完完全全的同步写法。


2、async用法

2.1 返回 Promise 对象

async函数返回一个 Promise 对象。

async函数内部return语句返回的值,会成为then方法回调函数的参数。

async function f() {
  return 'aaa';
}

f().then(v => console.log(v))
//aaa
//Promise {<resolved>: undefined}


2.2 await 命令

正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

/*成功情况*/
async function f() {
  return await 123;
}
f().then(value => console.log(value));  // 123
/*失败情况*/
async function f() {
  return Promise.reject('error');
}
f().catch(e => console.error(e));   // error

注意事项:

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

/* 错误处理 */
function f(db) {
  let docs = [1, 2, 3];
  for(let doc of docs) {
    await db.push(doc);
  }
  return db; // Uncaught SyntaxError: Unexpected identifier
}

/* 正确处理(顺序执行) */
async function f(db) {
  let docs = [1, 2, 3];
  for(let doc of docs) {
    await db.push(doc);
  }
  return db;
}


2.3 async中异常处理

通过使用 async/await,我们就可以配合 try/catch 来捕获异步操作过程中的问题,包括 Promise 中reject 的数据

await后面可能存在reject,需要进行try…catch代码块中

async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
    console.error(e);
  }
  return Promise.resolve('hello');
}
f().then(v => console.log(v));   // 出错了 hello


3、并联中的await

async/await 语法确实很简单好用,但也容易使用不当,还要根据具体的业务场景需求来定。

例如我们需要获取一批图片的大小信息:

async function allPicInfo (imgs) {
  const result = [];
  for (const img of imgs) {
    result.push(await getSize(img));
  }
}

代码中的每次 getSize 调用都需要等待上一次调用完成,同样是一种性能浪费,而且花费的时间也长。同样的功能,用这样的方式会更合适:

async function allPicInfo (imgs) {
  return Promise.all(imgs.map(img => getSize(img)));
}

多个异步操作,如果没有继承关系,最好同时触发。


4、总结

从最早的回调函数,到 Promise 对象,每次都有所改进,但又让人觉得不彻底。它们都有额外的复杂性,都需要理解抽象的底层运行机制。例如有三个请求需要发生,第三个请求是依赖于第二个请求的结果,第二个请求依赖于第一个请求的结果。若用 ES5实现会有3层的回调,导致代码的横向发展。若用Promise 实现至少需要3个then,导致代码的纵向发展。然而,async/await 解决了这些问题。

从实现上来看 async/await 是在 生成器、Promise 基础上构建出来的新语法:以生成器实现流程控制,以 Promise 实现异步控制。但是,不要因此小看 async/await,使用同步的方式写异步代码其实非常强大。

async/await 在语义化、简化代码、错误处理等方面有很多的优势,毕竟用async/ wait编写条件代码要简单得多,还可以使用相同的代码结构(众所周知的try/catch语句)处理同步和异步错误,所以常被称为JavaScript异步编程的终极解决方案,可见其重要性和优势。


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/3331

相关推荐

在线运行编程网站

学编程的程序员小伙伴越来越多了,在学习的过程中会发现很多知识点如果不在工作中运用或者手写带验证的话,很容易忘记。任何技能的掌握都是需要不断练习的。在此整理一些在线运行编程的网站。

前端的编程软件哪些比较好用?

推荐8款最好用的前端开发工具供美工或者前端开发人员使用,当然若你是NB的全栈工程师也可以下载使用。Web前端开发最常见的编程软件有以下几种: 在前端开发中,有一个非常好用的工具,Visual Studio Code,简称VS code

程序员的笔记,编程写软件学到的 7 件事

如果你真的做出了一些东西,在面对那些令人眼花缭乱的理论知识,或是和你相似甚至比你做的更糟糕的人时大可不必谦虚。在一天结束之时,正是那些在战壕中的开发者——构建、测试和开发了代码的人,真正做了事情。

十大编程语言榜单

如果你是软件开发领域的新手,那么你会想到的第一个问题是“如何开始?”编程语言有数百种可供选择,但是你怎么发现哪个最适合你,你的兴趣和职业目标又在哪里呢?选择最佳编程语言以学习的最简单方法之一,是通过市场反响、技术趋势的发展…

Blockly - 来自Google的可视化编程工具

Google Blockly 是一款基于Web的、开源的、可视化程序编辑器。你可以通过拖拽块的形式快速构建程序,而这些所拖拽的每个块就是组成程序的基本单元。可视化编程完成

感觉自己不适合做程序员,什么样的人不适合做程序员?

学习是一件艰苦的事情。很多人想要成为程序员,在学习编程的过程中,面对各种 bug 和源源不断的问题,有时会对自己是否适合编程这一问题产生困扰。在教学的过程中,他总结出了不适合做程序员的十个特征

CSS并不是真正的编程语言

每隔几个月就会出现一篇文章表明:CSS并不是真正的编程语言。以编程语言的标准来说,CSS过于困难。使用这门语言会很有创造性:事实确实如此,CSS不同于传统的编程,且具有缺陷,同任何标准化编程语言相比

自学编程的网站有那些?

如今,很大一部分人在学习编程的道路上都选择自学,但都苦于找不到适合自己的学习资源,今天为大家介绍11个免费的自学编程的网站,为大家提供一些帮助。很多人自学编程,都会通过w3school,你可以通过它学习所有的网站建设基础教程

无码编程:无代码软件开发大行其道

代码是大多数软件程序和应用程序的骨干。每行代码充当一条指令:采用一种逐步性的逻辑机制,以便计算机、服务器和其他机器执行操作。想创建那些指令,就要知道如何编写代码,这项宝贵的技能有时很吃香。

自学编程的六个技巧总结

这些事情可以帮助新手在他们漫长的旅程中学习编程。我知道我还有更多东西需要学习,并将继续学习如何永远地学习。最重要的事情说三遍,请继续,不要放弃,不要放弃,不要放弃。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!