1,了解JS事件循环、代码执行顺序,先了解运行时的函数调用栈(Stack)、堆(Heap)和任务队列(queue,有微任务队列和宏任务之分);(注:以下的理解不够深入,日后有机会再探讨)
Stack:栈的特点是先进后出,推进栈的每一个元素称为帧,每个函数的执行将作为一个帧被推进栈中,当函数执行完毕,那么该帧就会出栈,所以所谓的栈也就是函数调用栈;当栈的所有函数执行完毕,栈也就清空,JS会执行Queue中下一个任务并将其推进栈中;
Heap:堆是散列的,用来表示一大块的内存区域,对象被分配在堆中;
Queue:队列的特点是先进先出,消息队列或者说任务队列,每个消息都有一个相关联的回调(函数回调);队列可分为两种,微任务队列(指小型消息回调,如promise的回调和事件触发的监听器回调,会放入微任务队列)和宏任务队列(相对微任务来说,如setTimeout和setInterval的回调会放入宏任务队列),当微任务队列没有任务需要执行时,会立即执行宏任务队列的任务,当微队列有不止一个任务(该类型的回调不管是否在执行该队列的任务,新加的任务都会直接插入队列,并在该次事件循环中被调用),那么会把所有的任务都执行完毕才会去执行宏队列的一个任务;不管怎样,当遇到执行时间比较长的任务时,会阻塞用户的交互,这时要考虑分成几个更小的任务;每一个消息的执行都是上一次栈被清空后的开始,即上一个消息的执行完毕;有点需要注意的是,当此时此刻需要被执行的任务才会放入队列中,其他的任务就像是放在等待区一样,没到时间则不会进入队列中;
事件循环:类似于
//
while(true){
while(queue.waitForMessage()){
queue.processNextMessage(); //会先优先处理微任务
}
}
setTimeout, setInterval, Promise(异步函数):
setTimeout(fn | 'code string', delay, ...args); setInterval(fn | 'code string', delay, ...ars);new Promise(fn).then(fn, fn).then(fn, fn)....catch(fn).finally(fn);
setTimeout和setInterval: 一般不使用 'code string' 的形式作为回调,因为不安全不易于调试(可能还有其他问题?);args是在回调函数调用时,做回调函数的参数的,一般不这样使用,因为存在兼容性问题;以下例子展示了代码执行顺序;
//
console.log('Start');
//宏任务1
setTimeout(function(){ //延迟时间默认为 0,立即放入宏任务队列中;
console.log('setTimeout callback 1');
});
var pro = new Promise(function(resolve){
//宏任务2
setTimeout(()=>{ //延迟时间默认为 0,立即放入宏任务队列中;
console.log('setTimeout callback 2');
resolve(true)
});
});
//微任务1
pro.then(function(){ //被放入微队列等待区,promise的状态变为fullfilled时,
//该回调放入队列中,等待调用;
console.log('promise callback 1');
//微任务3
pro.then(function(){ //promise的状态已是fullfilled时,该回调放入队列中,等待调用;
console.log('promise callback 2');
});
console.log('promise callback 1 end');
});
//微任务2
pro.then(function(){//被放入微队列等待区,promise的状态变为fullfilled时,
//该回调放入队列中,等待调用;
console.log('promise callback 3');
});
//宏任务3
setTimeout(function(){
console.log('setTimout callback 3');
});
var i = 0;
var test = setInterval(function(){
console.log('hihi');
i++;
if(i> 2){
clearInterval(test);
}
},1000);
console.log('end');
//JS会先把这段执行完毕,微队列暂时没有任务,Start
//执行结果:
//Start
//end
//--------该函数调用结束,调用栈被清空,进入下一次事件循环; 此时微队列没有任务待调用,
//宏队列有任务待调用:宏任务1、2、3
//setTimeout callback 1
//setTimeout callback 2
//-------------resolve(true), 之后,promise的回调被推入微队列中,在一次事件循环,
//调用微队列的任务:微任务1、2、3
//promise callback 1
//promise callback 1 end
//promise callback 3
//promise callback 2
//-------在下一次事件循环中,微队列已空,调用宏队列的任务,此时剩下宏任务3;
//setTimout callback 3
//-----setInterval执行之后1秒钟,第一个setInterval回调进入宏任务等待调用
//hihi
//------又1秒之后
//hihi
//----又1秒之后
//hihi
2,其他文档:
3,参考文档: