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](https://img-blog.csdnimg.cn/img_convert/e6fbdf2792c59ab47192b17c9e664479.png#clientId=u51abf0c6-46ab-4&crop=0&crop=0&crop=1&crop=1&errorMessage=unknown error&from=drop&height=378&id=u0ca25af6&margin=[object Object]&name=sj.png&originHeight=896&originWidth=1500&originalType=binary&ratio=1&rotation=0&showTitle=false&size=347196&status=error&style=none&taskId=u2628d666-e97d-4629-a5c2-01d1c5922e8&title=&width=632)
异步任务又细分为宏任务和微任务
异步任务中。我们通常把宿主发起的任务称为宏任务,把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