js的执行机制 Event Loop(事件循环)

javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。所以一切javascript版的"多线程"都是用单线程模拟出来的。

1.js是一门单线程语言

2.Event Loop是javaScript的执行机制

1js是一门单线程语言意思就是说,js的执行顺序是从代码上往下执行的,下面看一段代码

    setTimeout(function(){
        console.log('定时器开始')
    });
    new Promise(function(resolve){
        console.log('Promise开始');
        resolve();
    }).then(function(){
        console.log('执行then函数')
    });
    console.log('代码执行结束');

运行结果:


image.png
javscript的同步和异步

单线程意味着,所有的任务都需要排队,只有等前面的任务结束,才能进行下一个任务,如果前面一个任务耗时太长,后面一个任务就绪等待很久。

如果排队是因为计算量大,cpu忙不过来也就算了,但是很多cpu是闲着的,因为IO设备(比如ajax请求),不得不等结果出来,再往下执行。

于是所有任务分为两种 1.同步任务(synchronous),2.异步任务(asynchronous)。

同步任务是指在主线程上执行的任务,只有当前面一个任务执行结束再执行后面的任务,存在一个先后执行的问题。异步任务指的是不进入主线程,而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

JS的事件循环(eventloop)是怎么运作的

事件循环、eventloop\运行机制 这三个术语其实说的是同一个东西,

运行过程

1.首先判断是同步还是异步,同步进入主线程,异步进入event table.
2.异步任务在 任务表(event table )注册后,当条件满足后(可能是延迟,也可能是ajax回调),被推入 任务列队( event queue)
3.同步任务进入主线程后一直执行,当主线程任务全部执行完成后,才会去eventqueue中寻找可以执行的任务,如果有就推入主线程。


image.png

什么时候知道主线程为空呢?js引擎存在monitoring process进程,会持续不断的检查 主线程 执行栈是否为空,一旦为空,就会去event queue那里检查是否有等待被调用的函数。

let data = [];
$.ajax({
    url:www.javascript.com,
    data:data,
    success:() => {
        console.log('发送成功!');
    }
})
console.log('代码执行结束');

1.ajax进入event table;
2.执行代码console.log('代码执行结束');
3.ajax完成,将回调函数success进入event queue;
4.js引擎存在monitoring process进程检测到主线程空闲,将success回调推入主线程并且执行。

setTimeout

setTimeout(() => {
  console.log('2秒到了')
}, 2000)

settimeout 首先进入event table,注册的时间就是它的回调,2秒后执行回调,将时间推入event queue,当主线程空闲的时候回去event queue中寻找是否有可执行的任务。

console.log(1) // 同步任务进入主线程  1
setTimeout(fun(console.log(3)),0)   // 异步任务,被放入event table, 0秒之后被推入event queue里  2
console.log(2) // 同步任务进入主线程  3

运行结果:


image.png

运行过程分析:
1.将console.log推入主线程 //1
2.遇到settimeout将settimeout推入event table,0秒后将回调推入event queue;
3.将console.log(2)推入主线程 //2
4.主线程执行完毕,处于空闲状态检测event tale是否有需要执行任务推入主线程 //3

setTimeout延时的时间有时候并不是那么准确

setTimeout(() => {
  console.log('2秒到了')
}, 2000)
wait(9999999999)

分析运行过程:
1.将setTimeout进入event table注册,并开始计时
2.执行sleep函数,sleep方法虽然是同步任务但sleep方法进行了大量的逻辑运算,耗时超过了2秒;
3.2000毫秒后将setTimeout回调推入event queue中,但是sleep还没执行完,主线程还被占用,只能等着。
4.sleep终于执行完了, console终于从event queue进入了主线程执行,这个时候已经远远超过了2秒;
注:其实延迟2秒,只是将settimeout的返回函数两秒后从evnet table中推入event queue中,只有当主线程空闲才会去执行 时间队列 (event queue)里的任务。

setIntval

以setIntval(fn,ms)为例,setIntval是循环执行的,setIntval会每隔指定的时间将注册的函数置入event queue,不是每过ms会执行一次fn,而是每过ms秒,会有fn进入event queue。需要注意一点的是,一单setIntval的回调函数fn执行时间超过了延迟事件ms,那么就完成看不出来有时间间隔了。

除了广义的同步任务和异步任务之分,我们对任务还有更精致的定义

宏任务

包含整个script代码块,setTimeout, setIntval

微任务

Promise , process.nextTick

注:不同类型的任务会进入不通的event queue,比如setTimeout, setIntval会进入宏任务的event queue,Promise , process.nextTick会进入微任务的event queue。

Promise与事件循环

注:promise在初始化的时候先会执行里面的函数,然后注册then回调。注册完成后,then不会执行,只有等同步代码执行完成后,再会到微任务的event queue里去查询是否有可用的promise回调,如果有,那么执行,如果没有,继续下一个事件循环。

宏任务执行图:

image.png

1.把一段代码看作是一个宏任务

2.在这个代码中遇到setTimeout, setIntval会进入宏任务的event queue,Promise , process.nextTick会进入微任务的event queue.

3.当这段代码的宏任务执行结束后,先查询微任务的event queue,如果存在微任务,执行完毕,在查询宏任务的event queue。如果没有存在的微任务。直接查询宏任务的event queue。

4继续执行下一个宏任务。形成一个循环就是事件循环。

下面用代码来深入理解上面的机制:

setTimeout(function () {
        console.log('4')
    })

    new Promise(function (resolve) {
        console.log('1') // 同步任务
        resolve()
    }).then(function () {
        console.log('3')
    })
    console.log('2')

运行结果:1>2>3>4
1.这段代码作为宏任务,进入主线程
2.先遇到setTimeout 将其注册后,然后将其分发到宏任务的event queue;
3.遇到Promise, new Promise 直接执行,将其then回调分发到微任务的event queue;
4.遇到console.log(), 立即执行;
5.整体script作为一个宏任务执行完毕,查看当前有没有可执行的微任务,发现then回调立即执行;(第一轮事件循环结束了,我们开始第二轮循环)
6.从宏任务的event queue开始,我们发现了宏任务event queue中setTimeout对应的回调函数,立即执行。 执行结果: 1-2-3-4

console.log('1')
setTimeout(function() {
    console.log('2')
    process.nextTick(function() {
        console.log('3')
    })
    new Promise(function(resolve) {
        console.log('4')
        resolve()
    }).then(function() {
        console.log('5')
    })
})

process.nextTick(function() {
    console.log('6')
})

new Promise(function(resolve) {
    console.log('7')
    resolve()
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9')
    process.nextTick(function() {
        console.log('10')
    })
    new Promise(function(resolve) {
        console.log('11')
        resolve()
    }).then(function() {
        console.log('12')
    })
})

执行结果:1>7>6>8>2>4>3>5>9>11>10>12

分析:
1.整体script作为第一个宏任务进入主线程,遇到console.log(1)输出1;
2.遇到process.nextTick,其回调函数被分发到微任务event queue中,我们记为process1
3.遇到setTimeout,其回调分发到宏任务event queue;我们暂且记为setTimeout1
4.遇到promies 先执行new promise 遇到 console.log('7') 输出7,then回调分发到微任务event queue;我们记为Promise1
5.遇到settimeout ,其回调分发到宏任务event queue 我们记为setTimeout2.
6.主线程执行完毕,处于空闲状态查找微任务event queue是否有任务,发现process1,Promise1,分别执行;输出6 8;(第一轮循环结束,第二轮循环开始)
8.首先输出2, 接下来遇到了process.nextTick(),统一被分发到微任务event queue,记为process2
9.new promise 立即执行,输出7. then分发到微任务event queue, 记为then2
10.现在开始执行微任务process2,和then2两个微任务可以执行分别输出 3 5(第二轮循环结束,第三轮循环set提settime2开始)
11.settime2输出9, process.nextTick分发进入微任务队列;我们记为process3
12new promise立即执行 ,输出11,then分发到微任务event queue 记为then3;
13现在开始执行微任务process3和then3 分别输出 10 12;查找宏任务event queue,里面不存在任务(三轮循环结束)

讲到这里那么有人会问settiemeout里面嵌套settimeout,那么嵌套的setTimeout的宏任务要在外面的宏任务排序的后面,往后排。看个例子

new Promise(function (resolve) { 
    console.log('1')// 宏任务一
    resolve()
}).then(function () {
    console.log('3') // 宏任务一的微任务
})
setTimeout(function () { // 宏任务二
    console.log('4')
    setTimeout(function () { // 宏任务五
        console.log('7')
        new Promise(function (resolve) {
            console.log('8')
            resolve()
        }).then(function () {
            console.log('10')
            setTimeout(function () {  // 宏任务七
                console.log('12')
            })
        })
        console.log('9')
    })
})
setTimeout(function () { // 宏任务三
    console.log('5')
})
setTimeout(function () {  // 宏任务四
    console.log('6')
    setTimeout(function () { // 宏任务六
        console.log('11')
    })
})
console.log('2') // 宏任务一

初步总结:

宏任务是一个栈按先入先执行的原则,微任务也是一个栈也是先入先执行。但是每个宏任务都对应会有一个微任务栈,宏任务在执行过程中会先执行同步代码再执行微任务栈。
一次事件循环就是一次宏任务+微任务执行完毕。

[所见即所得,更直观的的操作,简单明了](http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353