当前位置: 首页 > 后端技术 > Node.js

深入JS异步性

时间:2023-04-03 14:10:44 Node.js

单线程JavaScriptJavaScript有一个基于事件循环的并发模型。在处理任何其他消息之前,每条消息都会被完全处理。这在推理您的程序时提供了一些很好的属性,包括这样一个事实:无论何时运行一个函数,它都不能被抢占,并且会在任何其他代码运行之前完全运行(并且可以修改函数操作的数据)。runYourScript();while(atLeastOneEventIsQueued){fireNextQueuedEvent();};让我们用setTimeout作为一个简单的例子:letstart=+newDate;setTimeout(functionTask1(){letend=+newDate;console.log(`Task1:Timeelapsed${结束-开始}ms`);},500);//500ms后,Task1会被插入事件队列//单线程setTimeout(functionTask2(){letend=+newDate;console.log(`Task2:Timeelapsed${end-start}ms`);/***使用while循环延迟函数完成3秒*这样会阻塞下一个的执行事件循环中的函数*即必须在主线程完成其任务后循环事件队列*/while(+newDate-start<3000){}},300);//300ms后,Task2会被插入事件队列while(+newDate-start<1000){}//主线程延迟完成1秒console.log('mainthreadends');//output://主线程ends//Task2:Timeelapsed1049ms//Task1:Timeelapsed3000mssetTimeout会将第一个参数(函数类型)放入事件队列。以下是上面代码中发生的事情:300ms后,Task2被插入事件队列。500ms后,Task1被插入事件队列。1秒后,主线程结束,控制台打印“mainthreadends”。主线程在完成手头的工作后将循环遍历事件队列。事件队列中的第一个函数是Task2(因为它最先插入)。因此Task2被获取并执行,导致控制台输出“Task2:Timeelapsed1049毫秒”。您看到的经过时间可能有所不同,因为setTimeout并没有完全处理您的函数的执行。它所做的只是将函数放入您设置的延迟的事件队列中。函数何时执行取决于事件队列中的排队函数,以及主线程的状态(是否有剩余任务在运行)。Task2完成后执行Task1。什么是异步函数JavaScript中的异步函数通常可以接受函数类型的最后一个参数(通常是称为回调),当函数完成时回调将被插入事件队列。由于回调在事件队列中,该函数是非阻塞的。异步函数可以保证以下单元测试始终通过:letfunctionHasReturned=false;asyncFunction(()=>{console.assert(functionHasReturned);});functionHasReturned=true;请注意,并非所有函数都带有回调参数是异步的。例如。Array.prototype.forEach是同步的。让before=false;[1].forEach(()=>{console.assert(before);});before=true;异步函数中的异常由于回调函数被放入事件队列并稍后使用它自己的调用上下文执行,因此使用try-catch机制包装异步函数将无法从回调中捕获异常。try{setTimeout(()=>{thrownewError('回调错误');},0);}catch(e){console.error('捕获回调错误');}console.log('try-catch块结束');在示例中,回调中抛出的异常未被我们自己的程序捕获(即catch块-控制台上未打印“捕获的回调错误”)。我们可以使用浏览器中的window.onerror和Node中的process.uncaughtException捕获这些未捕获的异常。如果我们想故意捕获错误,就像我们应该经常做的那样,我们可以在回调中进行。例如让fs=require('fs');fs.readFile('abc.txt',function(err,data){if(err){returnconsole.error(err);};console.log(data);});ReferenceAsyncJavaScriptNotice如果你想关注最新的系列读书笔记的新闻/文章,请「收看」订阅。