我所理解的Event-loop

javaScript的执行机制(Event-Loop)

详细内容请关注我的博客地址

理解js的执行机制是一件至关重要的事情

var a = 10;
var b = 20;

console.log(a);
console.log(b);
//输出: 10 20

当我们接触到这样的代码,我们心情舒畅,因为一眼就可以看出我们应该先执行那个步骤,再执行哪个步骤,随后我们根据需求添加了定时器

var a = 10;
var b = 20;
setTimeout(() => {
  console.log(a);
}, 1000);
console.log(b);
// 20 10

我们遇到了小小的麻烦,执行过程需要考虑定时器的使用,这还算可以接受,但是随着需求增加我们使用定时器asyncpromiseawait等等异步操作的次数也会越来越多,我们会觉得执行更加复杂,甚至心态爆炸,因此我们必须理解js的执行机制,才能有效解决这个难以处理的问题

javaScript的事件循环

我们总会听到js是一门单线程语言,虽然随着语言的发展,技术大牛们也在探索多线程的发展,但是至今为止所有的类似多线程都是用单线程模拟出来的

由于Js是单线程,所以正常情况下代码是顺序执行的,但是很多时候有些代码不需要直接执行,因此我们把javascript中单线程任务分为以下两类:

  • 同步任务
  • 异步任务

任务队列

任务队列task Queue,即队列,是先进先出的数据结构

MacroTask(宏任务)

  • js的全部代码,setimeoutsetIntervalsetImmediateI/OUI Rendering

MicroTask(微任务)

  • Process.nextTick()PromiseObject.observe(废弃)MutationObserver

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HOF74brR-1592040777428)(https://user-gold-cdn.xitu.io/2017/11/21/15fdd88994142347?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)]

从以上导图可以看出:

  • 同步与异步任务分别在不同场所执行,同步任务进去主线程,异步任务进入Event Table并为异步任务注册回调函数
  • 任务完成后EventTable会将任务放入EventQueue
  • 主线程内的任务执行完毕后任务队列此时为空,任务队列会读取Event Queue中的任务加入队列,进入主线程执行
  • 以上过程不断重复,即为事件循环(Event Loop)

js引擎中存在monitoring process进程,它会不断地对主线程任务栈进行检查,一旦任务栈为空,则会去Event Queue检查是否有等待调用的函数

Event Table中的执行过程

执行栈在执行玩同步任务后,查看执行栈是否为空,如果执行栈为空,则查微任务(microTask)栈是否为空,如果不为空,则执行完成所有的微任务,如果为空则执行宏任务(Tasks)。

每一次宏任务执行完毕,都检查微任务队列是否为空,不为空则按照出对原则(先入先出)执行所有微任务,然后设置微任务队列为null,然后执行宏任务,如此循环

简单示例

Ajax

ajax技术是最常用到的数据异步数据请求技术,作为异步请求,他的执行顺序完全符合上面的流程

let data = [];
$.ajax({
    url: "www.sleepygod.xyz",
    method: post,
    data: data,
    success: () => {
        console.log("数据传输成功!")
    }
})
console.log("代码执行结束")

上面的代码执行顺序就是:

  1. ajax为异步任务进入Event Table,注册回调函数success
  2. 顺序执行主线程任务打印 代码执行结束
  3. Event Table中ajax执行结束,success被放入Event Queue
  4. 主线程执行完毕,任务栈为空,主线程从Event Queue读取success函数放入主线程执行
  5. 此时打印数据传输成功

setTimeout

setTimeout是延时执行函数,同样属于异步操作,总见到有人将setTimeout的延时设置为0,其实根据HTML标准,无论怎么设置,最低标准等待时间都是4ms

console.log("start")
setTimeout(() => {
    console.log("延时操作")
}, 3000)
console.log("end")

上面代码的执行过程是这样的:

  1. 打印start
  2. 定时器为异步操作,放入Event Table, 注册回调函数
  3. 主任务栈中执行打印end
  4. Event Table中定时器执行完毕,打印操作放入Event Queue
  5. 主任务栈为空,Event Table中的任务放入主线程执行,打印延时操作

复杂示例

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});
console.log('script end');

执行上述代码时,我们先将代码进行分类:

Tasks: run script、setTimeout callback //宏任务

MicroTask: Promise.then() //微任务

Js stack: script
log: script start, script end

执行同步代码: 打印script start,script end划分宏任务与微任务进行详细划分

Tasks: run script、setTimeout callback //宏任务

MicroTask: Promise1.then() //微任务

JsTask: Promise1
log: script start, script end,Promise1

宏任务执行完毕后,任务栈为空,查询微任务,发现Promise,执行Promise,打印promise1,将.then()放入微任务

Tasks: run script、setTimeout callback //宏任务

MicroTask: //微任务

JsTask: Promise1.then()
log: script start, script end,Promise1, Promise2

继续执行微任务,打印Promise2,微任务栈随即清空

Tasks: setTimeout callback //宏任务

MicroTask: //微任务

JsTask: setTimeout callback
log: script start, script end,Promise1, Promise2,setTimeout

继续执行宏任务,执行setTimeout callback打印setTimeout,随即宏任务与微任务全部清空

async/await

javascript在底层已经将async/await转换为promisethen回调函数

终极示例

这是一个摘自掘金作者作者:ssssyoki文章中的例子,我研究过后才真正理解了Event Loop的机制。

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')
    })
})

上述代码的执行顺序如下:

  • 首先执行同步任务 :

    Tasks: script, setTimeout1 callback, setTimeout2 callback  //宏任务
    
    MicroTasks: process.nextTick1, Promise.then()//微任务
    
    JsTask: script
    log: 1,7
    
  • 同步任务执行结束查询微任务栈,发现微任务,执行微任务:

    Tasks: script, setTimeout1 callback, setTimeout2 callback  //宏任务
    
    MicroTasks: //微任务
    
    JsTask: Promise.then()
    log: 1,768
    
  • 微任务执行结束,执行宏任务:

    Tasks: setTimeout1 callback, setTimeout2 callback  //宏任务
    
    MicroTasks: process.nextTick, Promise.then()//微任务
    
    JsTask: setTimeout1 callback
    log: 1,768,2,4
    
  • 宏任务执行结束,查询并执行微任务:

    Tasks: setTimeout1 callback, setTimeout2 callback  //宏任务
    
    MicroTasks: //微任务
    
    JsTask:  Promise.then()
    log: 1,768,2,435
    
  • 微任务执行结束,继续执行宏任务,发现setTimeout2:

    Tasks: setTimeout2 callback  //宏任务
    
    MicroTasks:  process.nextTick, Promise.then()//微任务
    
    JsTask:  setTimeout2 callback 
    log: 1,768,2,435,9,11
    
  • 宏任务执行结束,查询微任务,执行并清空微任务:

    Tasks: setTimeout2 callback  //宏任务
    
    MicroTasks: //微任务
    
    JsTask: 
    log: 1,768,2,435,9,11,10,12
    

    process.nextTick属于微任务又优先于所有微任务,有process.nextTick存在时应优先执行process.nextTick。

总结

javaScript时一门单线程的语言,js的执行机制为事件循环(Event Loop),理解了这一概念将会更有利于之后与js相关知识的学习。

javaScript在浏览器和node中的运行机制并不一致,以上的机制只适用于浏览器环境,并不适用于node环境-

MicroTasks: //微任务

JsTask: 
log: 1,7,6,8,2,4,3,5,9,11,10,12

process.nextTick属于微任务又优先于所有微任务,有process.nextTick存在时应优先执行process.nextTick。

总结

javaScript时一门单线程的语言,js的执行机制为事件循环(Event Loop),理解了这一概念将会更有利于之后与js相关知识的学习。

javaScript在浏览器和node中的运行机制并不一致,以上的机制只适用于浏览器环境,并不适用于node环境-

详细内容请关注我的博客地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值