为什么呈现事情循环

在了解阅读事情循环之前,咱们首要要弄理解为什么会呈现事情循环?为什么会发生音讯行列

第一版线程模型

  • 使命 1:1+2
  • 使命 2:20/5
  • 使命 3:7*8
  • 使命 4:打印出使命 1、使命 2、使命 3 的运算成果
void MainThread(){
     int num1 = 1+2; // 使命 1
     int num2 = 20/5; // 使命 2
     int num3 = 7*8; // 使命 3
     print(" 终究计算的值为:%d,%d,%d",num,num2,num3); // 使命 4
  }

当咱们需求处理以上四个使命的时分,咱们会把以上使命按照次序写进主线程里,等线程履行时,这些使命会按照次序在线程中依次被履行;等一切使命履行完结之后,线程会自动退出。

第二版线程模型(引进事情循环)

在使命处理进程中,不是一切使命都是提前准备好的。咱们可能在使命履行的进程中,接受到一个新的使命,这时分咱们就要引进事情循环,来监听是否有新的使命。

阅读器中的事情循环

现在的线程模型是这样

阅读器中的事情循环

这时分,咱们就发现了一个问题。烘托进程会频繁的接受到IO线程的音讯,就会形成页面烘托的卡顿。那么咱们如何改善呢?

第三版线程模型(引进音讯行列)

阅读器中的事情循环

经过改造之后,使命履行可分为三个步骤

  • 添加一个音讯行列
  • IO线程会发生使命放进队尾
  • 烘托主进程会循环轮询音讯行列

区分宏使命和微使命的必要性

了解了什么是时刻循环之后,咱们来谈谈为什么要有宏使命和微使命?

处理事情的优先级

比如当咱们想要监听DOM元素的删除和修改等,当DOM元素发生变化的时分,咱们应该是想立刻看到页面的变化。假如咱们把DOM元素的优先级设置成最高,直接放到音讯行列的队首,当操作DOM的时刻很长时,其他使命就会在音讯行列中长时刻的等候,形成功率的下降。假如咱们放到队尾,又会影响实时性,由于音讯行列中可能有很多使命在等候中。

针对这种状况,宏使命和微使命就应运而生。咱们将音讯行列中的使命称为宏使命,每个宏使命在履行的进程中会发生相应的微使命行列。宏使命履行完结之后,烘托引擎并不会着急履行下一个宏使命,而是履行当前宏使命发生的微使命行列。

事情循环的履行进程

当咱们大致了解了事情循环和宏使命,微使命之后,咱们来了解一下事情循环详细的履行进程。

宏使命

(macro)task首要包含:script(全体代码)、setTimeout、setInterval、I/O、UI交互事情、postMessage、MessageChannel、setImmediate(Node.js 环境)

微使命

microtask首要包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)

事情循环履行进程

阅读器中的事情循环

代码输命题

单独来讲的话太抽象了,咱们直接来看几个代码输命题吧

题一

console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')

首要,script(全体代码)是一个宏使命,先履行的是全体代码,先输出 ‘script start’ ,然后再履行new Promise里边executor函数, 输出 ‘promise1’ ,再输出 ‘promise1 end’ 然后履行resolve()。 然后将then()里边的函数添加到微使命行列中,再履行setTimeout,并将添加到下一个宏使命行列里边。 此刻, script全体代码(宏使命)履行完毕,然后再履行微使命行列,输出 ‘promise2’ 。此刻微使命履行完毕, 履行下一个宏使命行列, 输出 ‘settimeout’。

阅读器中的事情循环

题二

在看题二之前,咱们再温习一下Promise

resolve(参数)有以下几种状况

  • 1.普通的值或者目标
  • 2.传入一个promise,那么当前Promise的状况由传入的Promise决议,相当于状况进行了移送
  • 3.传入一个目标,而且这个目标中有then办法(thenable),那么也会履行该then办法,而且由该then办法决议后续状况

第二种状况,传递一个promise,promise的状况由传入的newPromise的状况决议

const newPromise = new Promise((resolve,reject) => {
    // resolve('aaa');
    // reject('err')
})
new Promise((resolve, reject) => {
    resolve(newPromise)
}).then(res => {
    console.log(res)
},err => {
    console.log(err)
})

第三种状况,传入目标中有then办法

new Promise((resolve,reject) => {
    const obj = {
        then:function (resolve,reject) {
            reject('err')
        }
    }
    resolve(obj)
}).then((res)=> {
    console.log('res',res)
},err => {
    console.log('err',err)
})
// 输出 err err

好了,咱们能够正式来看第二题了

new Promise(resolve => {
    resolve(1);
    Promise.resolve().then(() => console.log(2));
    console.log(4)
}).then(t => console.log(t));
console.log(3);

都看到这里了,相信大家肯定是知道先输出4 3 ,那么2 1是谁先输出呢?

在阮一峰老师的Es6中,Promise.resolve()办法答应调用时不带参数,直接返回一个resolved状况的Promise 目标。

需求留意的是,立即resolve()的 Promise 目标,是在本轮“事情循环”(event loop)的结束时履行,而不是鄙人一轮“事情循环”的开始时。 es6.ruanyifeng.com/#docs/promi…

这段代码的流程大致如下:

  1. script 使命先运转。首要遇到Promise实例,构造函数首要履行,所以首要输出了 4。此刻 microtask 的使命有t2t1
  2. script 使命继续运转,输出 3。至此,第一个宏使命履行完结。
  3. 履行一切的微使命,先后取出t2t1,别离输出 2 和 1
  4. 代码履行完毕

综上,上述代码的输出是:4321

为什么t2会先履行呢?理由如下:

  • 根据Promises/A+标准:

实践中要确保 onFulfilled 和 onRejected 办法异步履行,且应该在then办法被调用的那一轮事情循环之后的新履行栈中履行

所以,t2t1会先进入 microtask 的Promise行列。

自己思考一下撒

setTimeout(()=>{
    console.log('setTimeout')
},0)
Promise.resolve().then(()=>{
    console.log('promise1')
    Promise.resolve().then(() => {
        console.log('promise2')
    })
})
console.log('main')
let thenable = {
    then: function(resolve, reject) {
        console.log(0)
        resolve(42);
    }
};
new Promise(resolve => {
    resolve(1);
    Promise.resolve(thenable).then((t) => {
        console.log(t)
    });
    console.log(4)
}).then(t => {
    console.log(t)
});
console.log(3);

自己看了些材料然后总结了一下事情循环,假如有不对的地方欢迎大家一起交流哈!!!

参阅文档

从一道题浅说 JavaScript 的事情循环 Issue #61 dwqs/blog (github.com)

前端工程师一定要懂哪些阅读器原理?-极客时刻 (geekbang.org)