一篇文章搞懂JavaScript运行机制

单线程的JavaScript:

众所周知JavaScript这门语言是单线程,可是为何要设计成单线程呢?明明多线程更加有效率。前端

这里咱们就要从JavaScript的用途来考虑,JavaScript是一门浏览器脚本语言,也就是说它须要操做DOM来更改页面展现,提供用户良好的上网体验。这时候单线程的好处就体现出来,不妨想象一下promise

若是JavaScript是多线程的语言,当一个线程正在进行一个DOM的删除操做,这是另外一个线程出来搞事情,进行这个DOM的修改操做。这种状况要怎么处理呢?这样的场景就会出现一些问题。浏览器

因此说JavaScript单线程的设计是从它的用途出发的,而且在之后也会一直坚持单线程的设计。bash


同步、异步:

单线程就像是你们在食堂排队打饭,若是想打到饭,那就必须等食堂大妈一个一个把排在前面的人的饭打完才能轮到你。可是,若是JavaScript真的是这样那就糟糕了。不妨想一下,若是我访问的这个网站有一个超清图片,加载很慢,难道是要用户等到图片彻底加载出来以后才能进行其余的操做嘛,显然如今咱们浏览网站并不会出现这样的状况,那这又是怎么一回事呢?多线程

那是由于JavaScript的任务分为同步任务异步任务两种:异步

  • 同步任务

        同步任务就是进入主线程的任务,必须排队一个一个按顺序的执行。函数

  • 异步任务

        开发者们认识到,像费时的IO操做,接口请求彻底能够不理他们,将他们暂时挂起,放入事件表(Event Table)中当,主线程中的任务执行完,再来“宠幸”它们。这种暂时挂起的任务就是异步任务。oop


回调函数:

每个异步任务都须要指定一个事件,例如当耗时的IO操做执行完以后,就会将它指定的这个事件添加到任务队列中,这个事件就是回调函数。学习

因此说咱们常常说的主线程开始执行异步任务了,其实主线程执行的是异步任务的回调函数。网站


Event Loop:


如今来分析一下上图中的事件执行顺序:

  1. 首先任务进入执行栈,会先来判断这个任务是同步任务仍是异步任务。
  2. 若是是同步任务则进入主线程来执行,若是是异步任务则进入到事件表中注册函数
  3. 当事件表中的异步事件执行完后会在事件队列中注册自身的回调函数
  4. 当主线程的任务执行完后会去事件队列中检查是否有须要执行的事件,若是事件队列中有任务,则进入主线程执行。

上述的过程会不断的重复执行,这个重复的过程就是咱们一般所说的事件循环机制(Event Loop),看下面代码:

console.log(1);

document.body.onclick = function () { console.log('2'); }

console.log(3);复制代码

JavaScript中的给DOM注册一个点击事件,这个点击事件其实就是一种异步任务,由于咱们不知道用户何时才会点击。


如今咱们根据上图来分析一下这段代码的执行:

1.首先主线程会将同步的console.log操做放在主线程中执行,

2.首先打印出1,3

3.同时将点击事件放入到事件表中,当用户点击body后,JS会在事件队列中注册点击的回调函数。

4.这时主线程任务执行完毕,去任务队列中检查是否有须要执行的任务,这是发现了点击body的回调函数,JavaScript就会将这个回调函数放在主线程中执行。

5.全部就打印出了1,3,2的结果。


宏任务和微任务

异步任务其实还能够细分为宏任务微任务,他们的区别就是执行顺序的不一样,下面咱们就讨论一下他们具体的执行顺序,在Event Loop中到底有什么不一样。

其实异步任务的执行是有两个执行队列的,一个是宏任务队列,一个是微任务队列,每次执行的时候,首先是去微任务队列中查看是否有须要执行的任务,而后再去宏任务执行队列中查看是否有须要执行的事件。


宏任务:总体的script代码,setTimeout,setInterval、setImmediate

微任务:promise,process.nextTick


咱们如今根据这个流程图来分析一下具体的执行顺序:


  1. 首先执行主线程中的script代码,也就是执行宏任务
  2. 当宏任务执行完毕后,查看微任务队列是否有须要执行的事件,若是没有则继续查看宏任务,若是有则将微任务队列中的事件所有执行完毕。
  3. 当微任务清空后,在继续检查宏任务队列是否有可执行的事件,这个循环的过程就是宏任务和微任务的循环过程。


光说不练假把式:

如今咱们来分析一段代码,看看输出的顺序是否符合上面的流程图:


  1. 按照上面的流程图,先检查同步代码也就是宏任务代码进行执行,输出1,6 ,这里须要注意(Promise声明中的代码是会当即执行的也就是同步代码)
  2. 检查微任务事件队列,这里面的微任务就是promise.then里面的代码,这时输出7。
  3. 微任务完成以后继续检查宏任务,这段代码里面的宏任务就是setTimeOut,因此接下来执行宏任务中的代码,也就是输出2,4,9,10,
  4. 而后再去执行微任务队列中的事件,输出5,11
  5. 在输出7的时候then方法里面注册了setTimeout宏任务事件,在输出2的时候也一样注册了宏任务setTimeout,因此再次检查宏任务的时候,按顺序输出8,3

因此上述代码最终的输出应该是:1,6,7,2,4,9,10,5,11,8,3


结尾

但愿你们看完这篇文章可以有收获,哪里写的不对也但愿各位大佬多加指点,本文章仅为记录前端小白的学习过程,谢谢你们!

相关文章
相关标签/搜索