1.javascript事件循环:
同步任务:直接通过主线程执行,如script代码
异步任务:进入Event Table,并注册回调函数——> Event Queue,等主线程的执行栈为空时候,读取Event Queue里面的函数就,进入主线程。如setTimeout,promise.then()等;
那怎么知道主线程的执行栈为空呢,JS引擎存在monitoring process进程,它会持续检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。
let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:() => {
console.log('发送成功!');
}
})
console.log('代码执行结束');
ajax进入Event Table,注册回调函数success。
执行console.log(‘代码执行结束’)。
ajax事件完成,回调函数success进入Event Queue。
主线程从Event Queue读取回调函数success并执行。
2.setTimeout和setInterval
setTimeout(() => { a() },3000)
b(10000000)//b代表一个执行需要耗时很久的函数
a进入Event Table,注册回调函数success。
执行b函数,但是b函数执行时间超出了3秒钟
3秒钟到了,success进入Event Queue,但是以为主线程没有执行完,只能等。
b函数执行完了,主线程从Event Queue读取回调函数success并执行。
有时候我们这样写:setTimeout(()=>{},0),即使主线程执行栈是空的,这个并不是立即0毫秒执行,setTimeout有最小时间间隔限制,HTML5标准为4ms,小于4ms按照4ms处理,但是每个浏览器实现的最小间隔都不同.setInterval最小时间间隔为10ms;
4.promise与process.nextTick();
宏任务:上面我们说到的script代码,setTimeout
,setInterval
。
微任务:promise.then(),process.nextTick();且preocess.nextTick
优先级大于promise.then
;
console.log('1');
setTimeout(function() {
console.log('2');
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
process.nextTick(function() {
console.log('3');
})
})
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 (微任务,process.nextTick():6优先于promise.then(): 8)
宏任务:2、4 (第一个setTimeout)
微任务:3、5 (微任务,process.nextTick():3优先于promise.then() :5)
宏任务:9、11 (第二个setTimeout)
微任务:10、12 (微任务,process.nextTick():10优先于promise.then() :12)
输出:
1
7
6
8
2
4
3
5
9
11
10
12
总结:
1.先是宏任务–>微任务–>宏任务–>微任务一直循环下去;
2.script代码为第一层宏任务,如果有setTimeout,setInterval,则他们的回调函数会成为第二层,第三层…第n层的的宏任务,一个setTimeout是一个宏任务(重点),而不是几个setTimeout合在一起为一个宏任务,所以9会在3,5之后
3.promise.then()和process.nextTick()是微任务,在执行完该一层的宏任务后执行,且process.nextTick()优先于promise.then();
4.同步代码执行顺序优先级高于异步代码执行顺序优先级;
5.new Promise(fn)中的fn是同步执行;
练习
延迟 0 毫秒的 setTimeout() 回调
与 setImmediate()
非常相似。 执行顺序取决于各种因素,但是它们都会在事件循环的下一个迭代中运行。
而传给process.nextTick()
的函数会在事件循环的当前迭代中(当前操作结束之后)被执行。 这意味着它会始终在 setTimeout 和 setImmediate 之前执行。
例子一:
setImmediate(function(){
console.log(1);
},0);
setTimeout(function(){
console.log(2);
},0);
new Promise(function(resolve){
console.log(3);
resolve();
console.log(4);
}).then(function(){
console.log(5);
});
console.log(6);
process.nextTick(function(){
console.log(7);
});
console.log(8);
输出:
3
4
6
8
7
5
1
2
例子二:
//加入两个nextTick的回调函数
process.nextTick(function () {
console.log('nextTick延迟执行1');
});
process.nextTick(function () {
console.log('nextTick延迟执行2');
});
// 加入两个setImmediate()的回调函数
setImmediate(function () {
console.log('setImmediate延迟执行1');
// 进入下次循环
process.nextTick(function () {
console.log('强势插入');
});
});
setImmediate(function () {
console.log('setImmediate延迟执行2');
});
console.log('正常执行');
输出:
正常执行
nextTick延迟执行1
nextTick延迟执行2
setImmediate延迟执行1
强势插入
setImmediate延迟执行2