JS事件轮询机制

:JS 是单线程的。那么,异步怎么处理。也就是说,如何用单线程解决“多线程的场景”。

:用事件轮训机制,也叫事件循环(event loop)。

       事件轮询 (eventloop) 是"一个解决和处理外部事件时将它们转换为回调函数的调用的实体(entity)"

 JS事件轮询

        1、js 是一门单线程语言,从上往下执行的,首先,主线程读取js代码,形成一个执行栈,此时是同步的环境。
        2、当主线程检测到异步操作,就会交给其他异步线程处理,然后继续执行主线程的的任务。
        3、异步任务执行完毕之后,判断异步任务的类型,异步任务可分成宏任务和微任务,像setTimeout、setInterval属于宏任务,promise.then属于微任务,不同的任务进入不同的队列,等待主线程空闲时候调用。
        4、当主线程的的同步任务执行完毕之后,开始执行微任务队列里面的所有微任务,执行完微任务,就执行宏任务队列里面所有的宏任务。
        5、执行完成之后,主线程开始询问任务队列里面是否还有等待的任务,如果有则进入主线程继续执行。

1.执行栈

        在js中,当很多函数被依次调用的时候,因为js是单线程的,同一时间只能执行一个函数,怎么办?不能同时来,得排个队,排个一子队,按照顺序来,那这个一子队的顺序,即哪个函数在前,哪个函数在后,谁来记录呢?js为此专门开辟了一个内存区域,起名叫做执行栈。在执行栈里,保存着即将要执行的函数。

        当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个函数,那么js会向执行栈中添加这个函数的执行环境(当我们调用一个函数的时候,js会生成一个与这个函数对应的执行环境(context),又叫执行上下文。这个执行环境中保存着这个函数的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。),然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。

2.任务队列

         以上说的是同步执行的情况,如果出现了异步(如发送ajax请求数据)执行,就需要用到事件队列,或者叫做任务队列(Task Queue)。

 所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

异步任务指的是,不进入主线程、而进入 " 任务队列 "(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

        “ 任务队列 " 是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空," 任务队列 " 上第一位的事件就会自动进入主线程。

3.宏观任务队列和微观任务队列

     1)宏观任务队列

 宏任务:(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

        包括:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)。

     2)微观任务队列

  微任务:microtask,可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。
所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。

        包括:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)。

        注意:当宏任务碰到了微任务,先取微任务里的任务。因为,微任务耗时少,快。

一些示例

        1.当只有宏任务

console.log(1);
// // 碰到setTimeout代码,会先把异步任务交给web api(计时,并把任务扔到任务队列)
setTimeout(function(){
    console.log(2);
},1000)
// 碰到setTimeout代码,会先把异步任务交给web api(计时,并把任务扔到任务队列) 

setTimeout(function(){
     console.log(3);
 },500)
 console.log(4);
// 以上代码的执行顺序是:1,4,3,2

        2.当只有微任务

console.log(1);
(new Promise(function(resolve,reject){
     console.log(2);//同步
     resolve();//也是同步,但是调用then里的方法时,会把then里的方法扔给webapi,webapi再扔给微任务队列。
})).then(function(){
     console.log(3);
})

 console.log(4);

// 以上代码打印结果: 1,2,4,3

        3.当宏任务碰到了微任务时

console.log(1); 
setTimeout(function(){
     console.log(2);
},0);
new Promise(function(resolve,reject){
   console.log(3);
   resolve();
}).then(function(){
    console.log(4);
});
console.log(5);

// 以上代码打印结果: 1,3,5,4,2

        4.当宏任务碰到多个微任务

//当微任务里的所有任务都执行完毕时,才执行宏任务里的任务
console.log(1); 
setTimeout(function(){
    console.log(2);
},0); 
new Promise(function(resolve,reject){
   console.log(3);
   resolve();
}).then(function(){
     console.log(4);
});

Promise.resolve(5).then(function(n){
   console.log(n);
})
console.log(6);
// 以上代码打印结果: 1,3,6,4,5,2

当宏任务碰到了多个微任务时。
宏任务队列和微任务队列执行思路和轮询机制不一样:
 1、微任务队列执行时,会把所有的任务全部执行完毕,才会轮询宏任务
 2、宏任务队列执行时:当执行完一个任务后,就会去轮询微任务队列。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小可乐吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值