Javascript 运行机制
Posted yangkangkang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Javascript 运行机制相关的知识,希望对你有一定的参考价值。
先看一下下面这段js代码:
console.log(\'1\'); setTimeout(function(){ console.log(\'2\'); },0); console.log(\'3\'); 请问打印的结果是什么?
这段代码看似很简单,但如果不了解javascript运行机制就很容易答错。正确的输出是:1 3 2
一、JavaScript运行机制:
想要弄懂javascript执行机制(运行机制),首先要了解关于js相关的知识点,如下:
1、理解JavaScript是单线程的概念
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。JavaScript的单线程与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户交互以及操作DOM。这就决定了它只能是单线程的,否则会带来复杂的同步问题。所以,为了避免复杂性,从一诞生开始JavaScript就是单线程,这已经成为了这门语言的核心特征,将来也不会变。
2、 javaScript的同步任务和异步任务
单线程就意味着,所有任务需要排队。前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就得一直等着。JavaScript语言的设计者认识到这个问题,将所有任务分成两种:一种是同步任务(synchronous),一种是异步任务(asynchronous)。
同步任务指的是:在主线程上排队执行的任务,只有前一个任务执行完成,才能执行后一个任务。
异步任务指的是:不进入主线程,而进入“任务队列(task queue)”的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
异步任务包括:定时器、网络请求、Promise
javaScript中同步任务和异步任务的执行机制流程如下(引用网上的一张图):
1、所有同步任务都在主线程上执行,形成一个执行栈。
2、异步任务进入Event Table并注册函数。当异步任务有了运行结果时,Event Table会将这个函数移入 Event Queue
3、主线程中任务执行完毕为空,系统就会按次序的读取Event Queue中异步任务,进入执行栈,开始执行
4、主线程不断重复上面的第三步。因为这个过程是循环不断的,所以又称为事件循环(Event Loop).
3、javaScript的宏任务和微任务
JavaScript的除了同步和异步任务外,对其任务还有更细的定义:宏任务(macro-task)和微任务(micro-task)。
宏任务:包括整体代码script,setTimout,setInterval
微任务:Promise,process.nextTick
整体代码(宏任务)执行完后(即主线程执行栈为空),就开始事件循环(上面提到的Event Loop)。每次循环都要看Event Queue中是否有微任务,如果有则执行完所有的微任务后再执行其中一个宏任务,一个宏任务执行完成后开始进行下一次的循环。下一次循环开始先执行所有的微任务然后再执行其中一个宏任务。依次循环下去。
javaScript中宏任务和微任务的执行机制流程如下:
四、异步任务有哪些?放入任务队列(或者事件队列)的时机
任务分为宏任务(macro)和微任务(micro),在任务队列中的执行书序:微任务优先于宏任务
异步微任务:
1.ES6中的Promise
放入任务队列的时机:异步操作成功(或者失败)后,将成功的回调函数(后者失败的回调函数)放入
放入任务队列的任务:将成功的回调函数(后者失败的回调函数)放入任务队列中
举例说明:
new Promise(function(resolve,reject){ resolve(); }).then(function(){ console.log(\'3\') });
new Promise()立即执行,then回调函数 function(){console.log(\'3\')} 放到Event Table中注册函数,当resolved()后,回调函数会放到微任务 Event Queue中,等待进入主线程执行
异步宏任务:
1.定时器(setTimeout和setInterval)
放入Event Queue 时机:在规定时间到达后
放入Event Queue 的任务:将回调函数放入宏事件队列中
举例说明:
setTimeout(fn,5000)
fn进入Event Table,开始计时,等到5秒后回调函数进入Event Queue.当主线程代码为空时,就会读取Event Queue 中的回调函数,进入主线程开始执行。
2.Ajax异步请求
放入Event Queue的时机:在axjax加载完成时
放入Event Queue 的任务: 将回调函数放入宏任务事件队列中
举例说明:
$.ajax({ url:\'\', data:{}, success:function(){ console.log(\'发送成功\') } })
ajax进入Event Table,当ajax请求成功后,回调函数success进入Event Queue 。当主线程代码为空时,就会读取Event Queue 中的success回调函数,进入主线程开始执行。
3.DOM事件
放入Event Queue 的时机:在用户操作事件完成后
放入Event Queue 的任务: 将回调函数放入宏任务事件队列中
举例说明:
ele.on(\'click\',fn,false);
fn 进入Event Table,当触发click事件后,会将fn放入Event Queue中。
五、分析下面代码,看看是否掌握了JS的执行机制
例1:
console.log(\'1\'); setTimeout(function(){ console.log(\'2\'); },1000); new Promise(function(resolve){ console.log(\'3\'); resolve();//Promise没有写resolve,then回调函数不会执行,为了测试直接执行 }).then(function(){ console.log(\'4\'); }) console.log(\'5\');
分析:
* 整体script作为第一个宏任务进入主线程,遇到console.log,输出1.
* 遇到setTimeout, setTimeout是异步任务,被放到了event table 。1s 中中后,将其回调函数放到宏任务Event Queue 中。我们暂且记为setTimeout1
*遇到Promise, new Promise是同步任务,直接 输出3; .then里的函数是异步任务,被放到了event table。当执行resolve后将then回调函数放到微任务Event Queue。我们暂且记为then1
* 遇到console.log(\'5\'),直接 输出5
* 到此主线程的任务执行完毕,第一轮事件循环宏任务结束。开始读取Event Queue中的事件,我们发现有一个微任务then1,所以先读取then1,输出4 。到此第一轮事件循环正式结束。
* 第二轮事件循环从setTineout1开始, 输出2。第二轮事件循环结束。
所以结果是:1 -> 3 -> 5 -> 4 -> 2
例2:
console.log(\'1\'); setTimeout(function(){ console.log(\'2\'); new Promise(function(resolve){ console.log(\'4\'); resolve(); }).then(function(){ console.log(\'5\'); }) }) new Promise(function(resolve){ console.log(\'7\'); resolve(); }).then(function(){ console.log(\'8\'); }) setTimeout(function(){ console.log(\'9\'); new Promise(function(resolve){ console.log(\'11\'); resolve(); }).then(function(){ console.log(\'12\'); }) }) console.log(\'13\');
分析:
1. 第一轮事件循环流程:
* 整体script作为第一个宏任务进入主线程,遇到console.log(\'1\'),直接输出1
* 遇到setTimout,将其回调函数放到宏任务Event Queue中。我们暂且几位setTimeout1.
* 遇到Promise。new Promise直接执行,输出7。then回调函数被放到微任务Event Queue中,我们暂且记为then1
* 遇到setTimeout,将其回调函数放到宏任务Event Queue中。我们暂且记为setTimeout2
* 遇到console.log(\'13\'),直接输出 13
* 到此第一轮事件循环宏任务结束。此时Event Queue中的任务情况: 宏任务: setTimeout1 setTimeout2;微任务:then1
* 开始执行事件队列上的所有微任务,我们发现只有then1一个微任务,所以直接执行then1,输出8
* 到此,第一轮事件循环正式结束。
2. 第二轮事件循环流程:
* 从事件队列中的宏任务setTimeout1开始执行,直接输出2
* 遇到promise, new Promise直接执行,输出4。 then回调函数被放微任务Event Queue中,记为 then2
* 到此第二轮事件循环宏任务结束。此时Event Queue中的情况: 宏任务:setTimeout2 ;微任务:then2
* 开始执行event Queue中的所有微任务,发现只有then2一个微任务。直接执行,输出5
* 到此,第二轮事件循环正式结束
3. 第三轮事件循环流程:
* 宏任务只剩下setTimeout2了,直接输出9.
* 遇到promise ,new Promise 直接执行,输出11。 将then回调函数放到微任务Event Queue中,记为then3
* 第三轮事件循环宏任务结束,开始执行微任务then3,输出 12
* 第三轮事件循环结束
3. 整段代码公鸡行了三次事件循环,输出:1 -> 7 -> 13 ->8 -> 2 ->4->5 -> 9 ->11->12
以上是关于Javascript 运行机制的主要内容,如果未能解决你的问题,请参考以下文章