完整高频题库仓库地址:https://github.com/hzfe/awesome-interview完整高频题库阅读地址:https://febook.hzfe.org/JavaScript相关问题异步编程方案有哪些JavaScript异步编程方案的优缺点?回答要点阻塞事件循环回调函数JavaScript是一种同步的、阻塞的、单线程的语言,一次只能执行一个任务。但是浏览器定义了异步WebAPI,在事件循环中插入回调函数,实现异步任务的非阻塞执行。常见的异步解决方案包括异步回调、定时器、发布/订阅模式、承诺、生成器、async/await和WebWorkers。深入知识点1.异步回调异步回调函数作为参数传递给后台执行的其他函数。当在后台运行的代码完成时,回调函数被调用以通知工作已完成。具体示例如下://第一个参数是要监听的事件类型,第二个是事件发生时调用的回调函数。btn.addEventListener("click",()=>{console.log("你点击了我!");constpElem=document.createElement("p");pElem.textContent="hello,hzfe.";document.body.appendChild(pElem);});异步回调是最常见的JavaScript异步逻辑编写和处理方式,也是最基本的异步模式。但是随着JavaScript的发展,异步回调的问题也不容忽视:回调表达异步过程的方式是非线性的、非时序的,理解成本高。回调会受到控制反转的影响。因为回调的控制权在第三方(比如Ajax),第三方调用了回调函数,无法判断调用是否如预期。多个嵌套回调创建回调地狱。2.定时器:setTimeout/setInterval/requestAnimationFrame可以异步运行代码。主要特点如下:setTimeout:函数在任意时间后运行,递归setTimeout可以保证JavaScript线程不阻塞时执行间隔相同。setInterval:允许重复执行一个函数并设置时间间隔,但不能保证执行间隔相同。requestAnimationFrame:一种以当前浏览器/系统的最佳帧率重复有效地运行函数的方法。一般用于处理动画效果。setInterval会在设置的时间间隔定时调用,setInterval中代码的执行时间也包含在内,所以实际的间隔小于设置的时间间隔。递归的setTimeout在调用时开始计算时间,可以保证多次递归调用的时间间隔相同。如果当前JavaScript线程阻塞,无法执行轮到setInterval,那么这个任务就会被丢弃。setTimeout阻塞后,不会被丢弃,空闲时会继续执行,但不能保证执行间隔。3.发布/订阅模式(publish-subscribepattern)发布/订阅模式是对象之间的一种一对多的依赖关系。当一个对象的状态发生变化时,所有依赖它的对象都会收到状态变化的通知。.上面的异步回调示例也是发布-订阅模式的一种实现。订阅btn的点击事件,当btn被点击时发送这条消息给订阅者,并进行相应的操作。classPubSub{constructor(){//存储所有订阅的事件类型和对应的订阅函数数组//key:value[]this.handlers={};}//订阅事件方法on(eventType,handler){if(!(eventTypeinthis.handlers))this.handlers[eventType]=[];this.handlers[eventType].push(handler);}//消息发布方法emit(eventType,...handlerArgs){this.handlers[eventType].forEach((v)=>{v(...handlerArgs);});}//unsubscriberemove(eventType,handler){//函数中没有传入具体的事件处理器,则移除该事件类型的所有订阅函数//如果有,移除订阅数组中对应的函数if(!handler){this.handlers[eventType].length=0;}else{constkey=this.处理程序[事件类型].findIndex((v)=>v===处理程序);if(key!==-1)this.handlers[eventType].splice(key,1);}}}consttest1=newPubSub();constfn1=(...data)=>console.log(data);test1.on("event1",fn1);test1.on("event1",(...data)=>console.log(`fn2:${data}`));test1.emit("event1","hzfe1","hzfe2","hzfe3");test1.remove("event1",fn1);//["hzfe1","hzfe2","hzfe3"]fn1print//fn2:hzfe1,hzfe2,hzfe3发布/订阅模式可以详细了解有多少种事件类型And每种类型对应的订阅事件,方便进一步监控4.PromisePromise提供了完成和拒绝两种状态来标识异步操作的结果,然后可以使用和catch分别对这两种状态进行跟踪和处理。与事件监听的主要区别在于,Promise只能成功或失败一次,一旦状态发生变化,就不能从成功切换到失败,反之亦然。如果Promise成功或失败,即使在事件之后添加成功/失败回调,也会调用正确的回调。Promise使用顺序的方式来表达异步,将回调的控制权交给值得信赖的Promise.resolve()。同时也可以使用链式流的方式来避免回调地狱的发生,解决异步回调的问题。但Promise也有缺陷:顺序错误处理:如果不设置回调函数,Promise链中的错误很容易被忽略。单一解析:Promise只能解析一次(完成或拒绝),对多触发事件和数据流支持不佳(支持的标准正在开发中)。Unabletoobtainstatus:当处于Pending状态时,无法知道当前进度处于哪个阶段(刚刚开始还是即将完成)。无法取消:一旦创建了Promise并注册了履行/拒绝功能,就无法取消履行。5、GeneratorGenerator函数是ES6提供的一种异步编程解决方案。语法与传统函数完全不同。最大的特点是可以控制函数的执行。一个简单的例子如下:function*helloHzfeGenerator(){yield"hello";产量“hzfe”;return"ending";}varhello=helloHzfeGenerator();hello.next();//{value:'hello',done:false}hello.next();//{value:'hzfe',done:false}hello.next();//{value:'ending',done:true}hello.next();//{value:undefined,done:true}Generator并不总是像普通函数一样运行到结尾,它可以在运行过程中通过yield暂停并完全保持其状态,然后通过next恢复运行。yield/next不仅仅是一种控制机制,还是一种双向消息传递机制。yield表达式本质上是暂停并等待一个值,下一次调用将一个值(或隐式undefined)返回给暂停的yield表达式。生成器Generator保持了顺序、同步、阻塞的代码模式,同时也解决了异步回调的问题。6.async/awaitasync/await是ECMAScript2017JavaScriptEdition的一部分,使异步代码更易于编写和阅读。通过使用它们,异步代码看起来更像同步代码。它有以下特点:async/await不能用于普通的回调函数。async/await和Promise一样,是非阻塞的。async/await使异步代码看起来像同步代码。async/await也有一个问题:await关键字会阻塞它后面的代码,直到Promise完成,就像执行同步操作一样。它可以让其他任务在这段时间内继续运行,但是它自己的代码会被阻塞。解决方案是将Promise对象存储在变量中同时启动,然后等待它们全部完成。具体参见快速异步等待。如果内部await等待的异步任务之间没有依赖关系,需要获取这些异步操作的结果,可以使用Promise.allSettled()同时执行这些任务并获取结果。7、WebWorkerWebWorker为JavaScript创建了一个多线程环境,让主线程创建一个Worker线程,将一些任务分配给Worker线程运行,处理后将结果通过postMessage传递给主线程。优点是可以在单独的线程中执行耗时的处理任务,让主线程(通常是UI)中的任务运行而不会阻塞/变慢。使用WebWorker需要注意三点:在Worker内部,不能访问主线程的任何资源,包括全局变量、页面DOM或其他资源,因为这是一个完全独立的线程。Worker与主线程之间的数据传递是通过消息机制进行的。使用postMessage方法发送消息;使用onmessage事件处理程序来响应消息。Worker可以创建一个新的Worker,新的Worker与父页面同源。当Worker使用XMLHttpRequest进行网络I/O时,XMLHttpRequest的responseXML和channel属性将返回null。WebWorker的主要应用场景:处理密集型数学计算排序大数据集数据处理(压缩、音频分析、图像处理等)高流量网络通信参考资料异步JavaScript使用WebWorker