1. Js的运行机制
介绍
众所周知JavaScript是一门单线程的语言,所以在JavaScript的世界中默认的情况下同一个时间节点只能做一件事情,这就造成了JavaScript这门语言的一些局限性,比如在我们的页面加载一些远程数据时,如果按照单线程同步的方式运行,一旦有HTTP请求向服务器发送,就会出现等待数据返回之前页面假死的现象。因为JavaScript在同一时间只能做一件事,这就导致了页面渲染和事件的执行无法同进进行。
关于同步和异步
关于上述的情况,我们知道在JavaScript的世界中应该存在一种解决方案,来处理单线程造成的诟病。这就是同步【阻塞】和异步【非阻塞】执行模式的出现。
同步(阻塞)
同步的意思是JavaScript会严格按照单线程(从上到下,从左到右的方式)执行代码逻辑,进行代码的解释和运行,所以在运行代码时,不会出现先运行4、5行的代码,再回头支; 行1、3行的代码的这种情况。比如:
1 | var a = 1 |
升级场景
1 | var a = 1 |
异步(非阻塞)
如上例子,我们明白了单线程同步模型中的问题,接下来引入异步模型介绍。
1 | var a = 1 |
这段代码的setTimeout定时任务规定了2秒之后执行一些内容,在运行当前程序执行到setTimeout时,并不会直接执行内部的回调函数,而是会先将内部的函数在另外一个位置保存起来,然扣继续执行下面的console.log进行输出,输出之后代码执行完成,然后等待2秒后执行之前保存起来了函数。
总结
JavaScript的运行顺序就完全单线程的异步模型:同步在前,异步在后。所有的异步任务都要等待当前的同步任务执行完毕之后才能执行。请看下面案例:
1 | var a = 1 |
JS的线程组成
上面几个例子我们大概了解了Js的运行顺序,那么为是这个顺序呢?这个顺序的原理是什么样的?我们应该如保更好更深的探究真相?带着这些问题,我们一起来了解一下浏览器的一个页面的实际线程组成。
在了解线程组成前要了解一点,虽然浏览器是单线程执行JavaScript代dcbg的,但是浏览器实际是以多个线程协助操作来实现单线程异步模型的,具体线程组成如下:
GUI渲染线程
JavaScript引擎线程
事件触发线程
定时器触发线程
Http请求线程
其他线程
按照真实浏览器线程组成分析,我们会发现实际上运行JavaScript的线程其实并不是一个,但是为什么说JavaScript是一门单线程的语言呢?因为这些线程实际参与代码执行的线程并不是所有的线程,如比GUI渲染线程为什么单独存在,这个是防止我们在html网页渲染一半的时间突然执行了一段阻塞式的JS代码而导致网页卡在一半停住的这种效果。在JavaScritp代码运行的过程中实际执行程序时同时只存在一个活动线程,这里实现同步异步就是靠多个线程切换的形式来进行实现的。
所以,我们通常分析时,将上面的线分线程归纳为下面两条线程:
【主线程】:这个线程用来执行页面的渲染,JS代码运行,事件触发等
【工作线程】:这个线程是幕后工作的,用来处理异步任务的执行
2. JavaScript的运行模型
从代码片段开发分析
1 | function task1(){ |
我们创建了四个函数代表4个任务,函数本身都是同步代码。在执行的时候会按照1,2,3,4进行解析,解析过程中我们发现任务2和3被setTimout进行了定时托管,这样就只能先运行任务1和4了。当任务1和4运行完毕后500毫秒运行3,1000毫秒运行2
当同步任务都执行完成时,执行异步任务
总结
通过图解我们就更清晰的能搞懂异步任务的执行方式了。
关于执行栈
执行栈是一个栈的数据结构,当我们运行单层函数时,执行栈执行的函数进栈后,会出栈销毁然后下一个进栈出栈。如下例子
1 | function task1(){ |