当前位置: 首页 > Web前端 > JavaScript

【JavaScript笔记】JavaScript异步编程

时间:2023-03-27 17:42:56 JavaScript

什么是同步和异步从时序的角度(见时序图)来看,程序运行有两种模式:同步和异步,那么什么是同步和异步呢?我的总结是:程序执行完(也可能是代码执行)立即返回结果,就是同步模式;不是立即返回结果,而是在以后的某个时间点返回结果,是异步模式;分别举个例子:同步模式:你去银行取钱,你的号码被叫(启动),经过一系列的程序(中间柜员操作系统赚钱的时候你一直在柜台等),你必须等你拿到钱离开柜台(执行结束)。异步模式:你去麦当劳吃饭,点单时(开始执行),下单付款后就可以离开(不需要等到点单完成),可以在旁边等,或者你可以刷刷手机(这里还有一个概念:阻塞和非阻塞,本文不讨论),几分钟后,叫号的时候,去取餐(整个结束)。总结一下重点:执行结果是否立即返回。同步和异步的含义我们写的大部分代码都是同步代码,比如同步场景:赋值、取值、运算、条件判断等,异步场景:文件读取、网络访问、事件等。我想到了一个比喻:同步代码是你问我答,异步代码是放鸽子传书。看起来同步代码更快,但是我们在性能调优的时候往往会考虑应用这种策略:将同步代码改为异步代码来提高效率。问题是,什么时候是同步的,什么时候是异步的?谁更快?先分析优缺点:同步代码:需要执行完毕,得到结果,才能进行下一步。等待时间取决于逻辑复杂度、I/O速度和其他因素。好处是调用完成后就可以得到结果。缺点是如果代码比较耗时,会阻塞代码的执行;异步代码:调用完成后,可以进入下一步,在以后的某个时间获取结果。优缺点与同步正好相反。好处是当代码耗时时,不会阻塞代码的继续执行。缺点是调用完成后无法同步获取结果,需要考虑以后的结果如何获取和处理。可以用排除法看哪些需要先异步,其余的同步。海量数据操作,I/O慢,不想长时间阻塞代码,比如仿真模型的执行,大文件的哈希。对于分布式应用,希望提高硬件利用率,把阻塞代码变成异步代码,比如电商系统中的消息队列(MQ)。不确定时间的结果,比如网络请求和用户事件,典型的例子是Ajax请求。定时任务,比如上面的setTimeout,是比较常见的异步场景,其他场景基本可以同步解决。谁更快的问题不取决于模式,而是取决于具体的代码、计算量、I/O的速度等等。JavaScript中的同步和异步JavaScript早期只是作为浏览器脚本使用。随着Ajax技术的普及和丰富的应用,JavaScript已经成为Web开发的核心组件。越来越有钱了。它仍然是一种排除方法。这里我们只看JavaScript中的几种异步模式。每种异步模式都有自己的特点。强烈推荐阅读《你不知道的JavaScript(中卷)》的第二部分。回调函数在Promise之前,异步只能通过回调函数来实现。回调是最早也是使用最多的异步方式。有的同学可能没有意识到它是异步的,会问什么是异步。参见示例:functiongetText(url,success){constxhr=newXMLHttpRequest();xhr.onreadystatechange=function(){if(this.readyState!==4)返回;如果(this.status==200){成功(this.responseText);}else{thrownewError(this.statusText);}};xhr.open('GET',url,true);xhr.send();}getText('/demo/hello.txt',functionsuccess(responseText){console.log(responseText);});这是最简单的基于回调函数的异步代码,一个基本的ajax实现,getText调用完成时不返回任何数据,数据在回调函数success以后获取。回调函数在web1.0时期已经足够了,但是在web2.0富应用出现之后,出现了很多不便:回调地狱、代码顺序问题、信任问题等等。PromiseES6提供了Promise和microtask的支持,让异步模式的使用更加方便高效。其实Promise也是一种回调。可以看到ES5的模拟实现,但是有一个重要的区别:ES6Promiseresolve后,会加入到microtask队列中。具体可以参考microtask相关的topic。下面是一个Promise的对比例子(可以和上面的回调函数对比):){if(this.readyState!==4)return;if(this.status==200){resolve(this.responseText);}else{reject(newError(this.statusText));}};xhr。打开('GET',url,true);xhr.send();});}getTextPromise('/demo/hello.txt').then((responseText)=>{console.log(responseText);});乍一看,感觉和回调函数差不多。简单场景下确实类似,只是回调函数不在参数中,而是在chain方法then的参数中。但是当场景复杂的时候,Promise的优势才能体现出来。//多个请求顺序执行getTextPromise('/demo/hello.txt').then((responseText)=>{console.log(responseText);returngetTextPromise('/demo/my.txt');}).然后((responseText)=>{console.log(responseText);returngetTextPromise('/demo/world.txt');}).then((responseText)=>{console.log(responseText);});//多个请求并发Promise.all([getTextPromise('/demo/hello.txt'),getTextPromise('/demo/my.txt'),getTextPromise('/demo/world.txt')]).then(([responseText1,responseText2,responseText3])=>{console.log(responseText1,responseText2,responseText3);});//多个请求竞争Promise.race([getTextPromise('/demo/hello.txt'),getTextPromise('/demo/my.txt'),getTextPromise('/demo/world.txt')]).then((responseText)=>{console.log(responseText);});Promise会优雅且易读,不会有回调地狱,代码顺序符合正常思维习惯,还有更多其他优点:无多次调用,良好的错误处理机制等。生成器(Generator)ES6还增加了一个新的异步工具:generator,参见下面的例子(摘自MDN):function*generator(i){yieldi;yieldi+10;}constgen=generator(10);console.log(gen.next().value);//预期输出:10console.log(gen.next().value);//预期输出:20Generator函数在执行过程中可以暂停和恢复,暂停时获取yield值,恢复时传值。生成器的特点是可以暂停生成器函数的语句,同时保持当前状态。您可以暂停多次。这与Promise不同。Promise只有未完成和完成(成功和失败)状态。生成器函数返回一个迭代器,控制代码通过该迭代器恢复执行并传递值。生成器的优点是它们保持异步代码的顺序并且看起来与阻塞同步代码一样。举一个最常见的应用场景:ID生成器。还有很多高级用法,推荐阅读《你不知道的JavaScript(中卷)》。async/awaitES7又增加了一个异步语法:async/await其实就是一个语法糖,可以让我们更方便的使用Promise。好处类似于生成器:使异步代码看起来像阻塞的同步代码,具有正常的逻辑顺序。functiongetTextPromise(url){returnnewPromise(function(resolve,reject){constxhr=newXMLHttpRequest();xhr.onreadystatechange=function(){if(this.readyState!==4)return;if(this.status==200){resolve(this.responseText);}else{reject(newError(this.statusText));}};xhr.open('GET',url,true);xhr.send();});}asyncfunctionasyncCall(){console.log('calling');constresult1=awaitgetTextPromise('/demo/hello.txt');控制台日志(结果1);constresult2=awaitgetTextPromise('/demo/world.txt');控制台日志(结果2);console.log('调用结束');}asyncCall();async/await之后,Promise对象就不需要使用then方法链来获取异步结果了,异步写法变成了同步写法,符合我们正常的逻辑思维顺序。总结异步模式的核心是连接现在和未来,处理好它们之间的关系。学习完上面的几种异步模式,你对Javascript异步编程有了一个基本的了解,最后:大量的实践和总结。