JS是单线程,所以理论是会出现阻塞的问题,为了解决该问题,所以通过单线程来模拟多线程进行解决。
同步任务:在程序中可以立即执行的任务,比如console.log等,可以立刻看到结果。
异步任务:在程序中需要等待才能看到结果的任务,比如接口请求、定时器、延时器等。
代码从上向下执行进行执行栈,会判断任务是同步还是异步。
同步任务:则进入主线程,同步任务全部执行完毕,获取任务结果。
异步任务:则进入任务表中注册函数,当指定的函数执行完成后,注册回调函数,任务表则将这个函数移到任务队列当中。主线程内任务全部执行完成后,会去任务队列中读取对应函数,进入主线程执行,
事件监听会监听异步任务的状态,如果可以执行回调,就会将对应的任务放到任务队列里。
上述过程会不断循环,这就是任务循环
宏任务与微任务
首先:宏任务与微任务都属于异步任务
先执行微任务后执行宏任务
2.宏任务与微任务有哪些
宏任务
I/O、setTimeout、setInterval、setImmediate
微任务
process.nextTick 、Promise.then/.catch/.finally
3.宏任务与微任务的执行顺序
JavaScript是单线程的,常用的任务分为同步任务和异步任务。在每轮事件循环中,主线程会先执行完同步任务,再执行异步任务。
整体JavaScript代码将作为一个宏任务执行,先将同步任务进入主线程执行,异步任务进入事件表(Event Table)并注册回调函数(如:success、then、catch等)。当异步事件完成,回调函数进入事件队列等待被调用。而来自不同任务源的任务会进入不同的任务队列。其中setTimeout与setInterval是同源的。
js引擎Monitoring Process进程会不断的检查主线程执行栈是否为空,一旦为空,就会检查事件队列中是否有等待被调用的函数,如果有,主线程将依次读取回调函数并调用。否则执行下一轮事件循环。
微任务存到一个数组中,红任务存到链表中。
在每轮事件循环中微任务队列的优先级高于宏任务队列。微任务队列中排队的所有微任务都在同一周期内处理,而这些微任务本身也可以将其他微任务添加到微任务队列中中执行,只有这些微任务全部执行完成时,才会执行下一个宏任务。
4.案例
案例1
setTimeout(function(){
console.log('1');
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3');
}).then(function(){
console.log('4')
});
console.log('5');
// 2 5 3 4 1
1.遇到setTimout,异步宏任务,放入宏任务队列中
2.遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2
3.而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
4.遇到同步任务console.log(‘5’);输出5;主线程中同步任务执行完
5.从微任务队列中取出任务到主线程中,输出3、 4,微任务队列为空
6.从宏任务队列中取出任务到主线程中,输出1,宏任务队列为空
案例2
(先同步后异步,先微任务,再宏任务)
console.log('script start'); // 同步
setTimeout(function () {
console.log('setTimeout1'); // 异步
}, 300);
setTimeout(function () {
console.log('setTimeout2'); // 异步
}, 0);
new Promise(resolve => {
resolve()
console.log('promise1'); // 同步
}).then(() => {
console.log('promise2'); // 异步
})
console.log('script end'); // 同步
// script start
// promise1
// script end
// promise2
// setTimeout2
// setTimeout1
process.nextTick()先于Promise.then()执行, setTimeout()与setImmediate()执行顺序取决于setTimeout的执行周期与设备性能。
process.nextTick 属于 idle观察者 idle观察者 > IO观察者 > check观察者
console.log(1);
setTimeout(function () {
console.log(2);
let promise = new Promise(function (resolve, reject) {
console.log(3);
resolve();
}).then(function () {
console.log(4);
});
}, 1000);
setTimeout(function () {
console.log(5);
let promise = new Promise(function (resolve, reject) {
console.log(6);
resolve();
}).then(function () {
console.log(7);
});
}, 0);
let promise = new Promise(function (resolve, reject) {
console.log(8);
resolve()
}).then(function () {
console.log(9);
}).then(function () {
console.log(10)
});
console.log(11);
1, 8 ,11 , 9 , 10 , 5 , 6, 7, 2, 3, 4
注意注意
不同版本的node 执行顺序不一样
function test () {
console.log('start')
setTimeout(() => {
console.log('children2')
Promise.resolve().then(() => {
console.log('children2-1')
})
}, 0)
setTimeout(() => {
console.log('children3')
Promise.resolve().then(() => {
console.log('children3-1')
})
}, 0)
Promise.resolve().then(() => {
console.log('children1')
})
console.log('end')
}
test()
注意: 以上代码在 node11 以下版本的执行结果(先执行所有的宏任务,再执行微任务)
start
end
children1
children2
children3
children2-1
children3-1
注意: 以上代码在 node11 及浏览器的执行结果(顺序执行宏任务和微任务)
start
end
children1
children2
children2-1
children3
children3-1