概念
js是基于单线程运行的,而一些特定事件又是异步执行的,所以这种单线程+异步的执行方式一定是事件驱动的 而一般浏览器环境下有这样几种线程。
js引擎线程 (解释执行js代码、用户输入、网络请求)主线程
GUI线程 (绘制用户界面、与js主线程是互斥的)先绘制dom再绘制css
http网络请求线程 (处理用户的get、post等请求,等返回结果后将回调函数推入任务队列)
定时触发器线程 (setTimeout、setInterval等待时间结束后把执行函数推入任务队列中)
浏览器事件处理线程(将click、mouse等交互事件发生后将这些事件放入事件队列中)
上一张经典的eventLoop图,了解几个基本概念
[图片上传失败...(image-a3d100-1517238113235)]
1,栈(stack):队列优先,由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2.堆(heap):先进后出;动态分配的空间
JavaScript中的变量分为基本类型和引用类型。基本类型就是保存在栈内存中的简单数据段,而引用类型指的是那些保存在堆内存中的对象
这里引入一个概念(图上没有),就是js中的task和microTask 一般来讲,我们把setTimeout,setInterval,浏览器的dom操作,用户点击等交互事件归结为task,而Promise callback Mutation callback还有nextTick我们归结为microTask,
那问题就来了,js中task和microTask执行顺序是怎样的
由于js是单线程,所有的多线程任务最后都要压入主线程执行栈去执行,如图所示,task会放入回调队列里 由eventLoop取出依次执行。
这里可以解释一个小问题,如果多个setTimeout的情况下,执行时间不是特别的精确,setTimeout 它会在延迟时间结束后分配一个新的 task 至 event loop 中,而不是立即执行,所以 setTimeout 的回调函数会等待前面的 task 都执行结束后再运行。
说到这里好像还没有microTask什么事,那microTask在什么时候执行呢,通俗的说,你可以将microTask理解为一个爱插队的大妈,就是在一个task事件结束后,microTask就插入执行栈立即执行,执行顺序是主线程执行栈的队尾插入,callback quene之前
上一段代码吧,来直观的了解一下执行顺序
console.log('start');
setTimeout(function() {
console.log('setTimeout1');
setTimeout(function() {
console.log('setTimeout2');
},0);
console.log('setTimeout3');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('end');
最后结果是 1,start 2,end 3,promise1 4,promise2 5,setTimeout1,6,setTimeout3 6,setTimeout2
顺序是,先执行打印start 然后打印edn 这时候microTask (promise)插队进入 打印promise1,promise2, 然后执行setTimeout打印setTimeout1,setTimeout3,这里可以看做
console.log('setTimeout1');
console.log('setTimeout3');
这两句一起压入执行栈,
setTimeout(function() {
console.log('setTimeout2');
},0);
进入task队列,由EventLoop取出,所以最后执行顺序是
1,start 2,end 3,promise1 4,promise2 5,setTimeout1,6,setTimeout3 6,setTimeout2