丁鹿学堂:js中异步任务原理总结

52 阅读3分钟

js这门语言创建的时候就是一门单线程非阻塞的语言。 但是按照我们的理解,单线程一定是会阻塞的。js单线程非阻塞的原理就是js里面设计了一个基于事件循环的并发模型。

事件循环负责执行代码,收集和处理事件,以及执行队列中的子任务。

js为什么要设计成单线程

js设计之初的用途就是与浏览器进行交互,当时主要就是dom操作,而dom操作如果不是单线程并且相互冲突的,会出现问题。比如一个线程让他去给dom添加点击事件,另一个线程让dom删除。所以js为了简单粗暴避免这些问题,就设计成了单线程。

web worker

webworker的技术,让js能执行多线程。但其实他有很多的限制。比如说新线程是受到主线程的完全控制,不能够独立执行。实际上他们只是主线程下面的子线程,它没有IO权限,只能为主线程分担一些计算的任务而已。

因为有些面试官喜欢问,js到底是不是单线程的。js是单线程的,但是可以利用web worker去做一些类似多线程的实现。

浏览器环境下的V8引擎的事件循环机制

js的事件循环机制,在浏览器和node中还是有差别的,node中更复杂。我们先了解浏览器中js的事件循环机制。

js中同步代码的执行过程

首先,解析执行js代码的时候,把变量根据类型放在不同位置,(堆内存和栈内存中)。当代码开始执行的时候,我们会把全局环境压入到执行栈中,从头到尾依次执行。

注意:比如,当调用函数fn的时候,js会向执行栈中添加fn的执行环境,然后会进入这个执行环境中去执行代码。执行环境也叫执行上下文。我们可以把他理解成一个集合。里面包含函数的私有作用域,参数,作用域里面的变量,作用域里面this对象。当fn函数中代码执行完毕,就把函数执行结果返回出来,js退出并销毁fn的执行环境。

js中的异步代码的执行过程

js引擎遇见异步事件的时候并不会一直等待结果,而是会将这个事件挂起。继续去运行执行栈中的其他同步任务。异步一般都有一个回调,当异步事件执行完返回结果的时候,js会把这个回调加入到一个事件队列中。当主线程的任务都执行完以后,会从事件队列里拿出来回调去依次执行。 sj.png

异步任务又细分为宏任务和微任务

异步任务中。我们通常把宿主发起的任务称为宏任务,把js引擎发起的任务称为微任务

宏任务:setTimeout,setInterval

微任务:Promise.then(function(){}), new MutaionObserver()

注意:Primise的then方法的回调才是异步的,promise里面的代码仍然是同步的。不要一看到primise就觉得全部是异步!

当去异步任务队列里执行的时候,先执行微任务,再执行宏任务。

setTimeout(()=>{
  console.log(1)
},0)

new Promise((resolve,reject)=>{
  console.log(2)
  resolve(3)
}).then(res=>{
  console.log(res)
})
console.log(4)  // 2 4 3 1