如何理解JS异步编程的,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务?

328 阅读5分钟

一. 为什么js会有异步编程?

众所周知,javascript设计初衷最早是为了实现页面上的动态交互,实现页面的核心是dom操作,是运行在浏览器中的脚本语言,所以决定了javascript是单线程运行的,但为什么我们常常又会听说js异步编程呢?

显而易见单线程运行意味着浏览器有多个任务的时候要排队依次进行,一个一个完成,这种模式的优点是比较安全。缺点是如果我们遇到一个特别消耗时间的任务,那么后面的任务就会一直等着这个任务的完成,这样会造成页面卡死的情况,会造成阻塞。为了解决这个问题,javascript讲执行任务分成了两种模式:同步模式和异步模式(也就是我们常说的异步编程)。

二. 什么是同步模式?

同步模式指的是我们的javascript代码要依次执行,后面的代码要等待前一句代码执行完成才能执行,排队执行,javascript代码大多数是以同步模式进行执行的。

三. 什么是异步模式?

异步模式指的是我们的javascript代码不会等待前面的代码执行完毕才开始执行。

我们将执行的代码放入到调用栈中执行,如果是同步的直接执行,如果是异步的则放入消息队列中等待执行,等到所有的代码执行完毕,我们的event loop就上场了,它会监听调用栈和消息队列中的任务,当调用栈中所有的任务结束以后,它会从消息队列中依次取出回调函数压入到调用栈,开始执行,直到整个循环结束。

四. 异步模式有哪些常见的实现方式?

1.setTimeout

常用于:定时器,动画效果 用法:setTimeout(func|code, delay)

优点:简单、容易理解。

缺点:滥用定时器可能会造成内存溢出。

2.传统callback回调函数

回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。虽然回调函数多用于异步编程,但带有回调函数的方法不一定是异步的。

优点:简单、容易理解。

缺点:不利于代码的阅读和维护,会出现“回调地域”,而且每个任务只能指定一个回调函数。

3.事件发布/订阅模式

发布订阅模式,它定义了一种一对多的关系,可以使多个观察者对象对一个主题对象进行事件监听,当这个主题对象发生改变时,依赖的所有对象都会被通知到。

优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数。

缺点:由事件驱动,运行流程变得很不清晰。

4.Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

优点:ES6将promise写进了语言标准,统一了使用的语法,使用简洁方便,比传统异步解决方案更合理强大。

缺点:状态不可逆;代码冗余,原来的任务被Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。

5.Generator

带星号function,yield语句 ,next() 获取下一个yield表达式中yield后的值,拥有遍历器接口,与for..of可搭配使用。

优点:Generator 可以暂停函数执行,返回任意表达式的值。这种特点使得 Generator 有多种应用场景。

缺点:流程管理却不方便,Generator 函数的执行必须靠执行器,如Co模块。

6.async/await

async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await。一句话,它就是 Generator 函数的语法糖。

优点:内置执行器;更好的语义;返回值是 Promise。

缺点:无法处理Promise返回reject对象,要借助try...catch;async/await中await只能串行,做不到并行(await不在同一个async函数里就可以并行)

五. EventLoop、消息队列

单线程意味着所有的任务必须排队依次执行,JS 的任务分为同步任务和异步任务,同步任务会进入主线程中依次执行,遇到异步任务则会将其放入消息队列中等待执行,当主线程任务执行完之后,执行栈清空,事件触发器会从消息队列中取出一个任务继续执行,执行完毕之后执行栈继续清空,然后重复执行上一步操作,直到全部执行完毕,这个机制称为 EventLoop。

消息队列用来处理异步任务。每当遇到异步调用事件都会将其放入队列中,执行完毕后由任务队列通知主线程,执行事件。

六. 宏任务,微任务

有一个大爷去银行存款,排队等到他去存款的时候突然想要开通手机银行,这时候银行柜员会优先完成大爷的需求再开始其他人的业务。

大爷去存款:宏任务

大爷要开通手机银行: 微任务

在当前的微任务没有执行完成时,是不会执行下一个宏任务的。

常见的宏任务: setTimeout, setInterval

常见的微任务: promise.then, process.nextTick