Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7. [Event Loop]宏任务和微任务 #7

Open
maxlxq opened this issue Dec 7, 2019 · 0 comments
Open

7. [Event Loop]宏任务和微任务 #7

maxlxq opened this issue Dec 7, 2019 · 0 comments

Comments

@maxlxq
Copy link
Owner

maxlxq commented Dec 7, 2019

案例,打印顺序

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

答案:

script start
script end
promise1
promise2
setTimeout

同步任务和异步任务会进入不同的执行场所,同步任务会进入到主线程,异步的进入Event Table并注册函数
当指定的事情完成时[如:setTimeout的倒计时事件结束],Event Table会将这个任务移入Event Queue
主线程内的任务执行完毕为空,会去Event Queue中读取对应的函数,放到主线程中执行
上述过程会不断的重复,就是Event Loop

微任务和宏任务皆为异步任务,它们都属于一个队列,主要区别在于他们的执行顺序,Event Loop的走向和取值。

而宏任务一般是:包括整体代码script,setTimeout,setInterval、setImmediate
微任务:原生Promise、process.nextTick、MutationObserver

setTimeout:异步可以延迟执行,如:setTimeout(() => console.log('延迟3秒执行'), 3000)

setTimeout(() => task(), 3000)
console.log('执行console')
sleep(10000)

结果:

// 执行console
// 执行sleep
// task()

解释:
task()进入Event Table并注册,计时开始
执行console.log函数
执行sleep函数,计时继续
3秒到了,计时时间setTimeout完成,task()进入Event Queue,等待sleep执行完成
sleep完成,task()从Event Queue进入主线程执行

关于setTimeout要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。有兴趣的同学可以自行了解

setInterval 循环执行,每隔定量时间将注册的函数fn()移入Event Queue

不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。

Promise

setTimeout(()=>{
  console.log('setTimeout1')
},0)
let p = new Promise((resolve, reject)=>{
  console.log('Promise1')
  resolve()
})
p.then(()=>{
  console.log('Promise2')    
})

输出:Promise1,Promise2,setTimeout1

Promise参数中的Promise1是同步执行的,其次是因为Promise是microtasks,会在同步任务执行完后去清空microtasks queue,最后清空完微任务再去宏任务队列取值。

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. 整体script作为第一个宏任务进入主线程,遇到console.log,输出 1;
  2. 遇到setTimeout,回调函数被分发到宏任务Event Queue中,记为 setTimeout1;
  3. 遇到process.nextTrck,回调函数被分发到微任务Event Queue,记为process1;
  4. 遇到Promise,new Promise立即执行,输出7,then被分发到微任务Event Queue中,记为then1;
  5. 遇到setTimeout,回调函数被分发到宏任务Event Queue中,记为setTimeout2;

| 宏任务 | 微任务 |
| setTimeout1 | process1 |
| setTimeout2 | then1 |

第一次事件循环结束,清空微任务队列,执行process1,输出6,执行then1,输出8;

第二次事件循环流程

  1. 将宏任务setTimeout2移入主线程执行,遇到console,输出2;
  2. 遇到process.nextTick,回调函数被分发到微任务Event Queue,记为process2;
  3. 遇到Promise,立即执行new Promise,输出4,then被分发到微任务Event Queue中,记为then2.

| 宏任务 | 微任务 |
| setTimeout2 | process2 |
| | then2 |

第二次事件循环结束,清空微任务队列,执行promise2,输出3,执行then2,输出5

第三次事件循环,将宏任务setTimeout2移入主线程,执行流程如上,输出:9、11、10、12

node环境:v12.13.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant