同步异步和Event loop事件循环
js 单线程模型
JavaScript 是单线程、非阻塞的一种语言,只有一个主线程,同时只能执行一个任务。
js 使用单线程是为了简单化
js 中的栈、堆和消息队列
栈
存放的是调用函数的记录——调用帧
堆
存放的是对象
消息队列
- 包含待处理消息的队列
- 每个消息都关联了一个回调函数,用以处理这个消息。
Event Loop
什么是事件循环Event Loop
Event Loop 是一种循环检查机制
- 当主线程的任务执行完以后,就会去消息队列查看是否有满足条件的异步任务(js 将在消息队列中的任务称为异步任务)。
- 如果有,就将该异步任务调入主线程成为同步任务,然后执行其关联的回调函数
- 然后不断循环,如果任务队列的任务全部执行完了,那么程序就结束。
主线程
同步任务和异步任务
同步任务
是没有被挂起,在主线程里排队执行的任务
异步任务
分为宏任务和微任务
宏任务
包括:
包括整体代码 script、setTimeout、setInterval、setImmediate、I/O、UI rendering
微任务
包括:
promise.then, process.nextTick(node中)
宏任务的执行顺序在微任务之前
事件循环的过程
任务执行时,首先执行整体代码这个【宏任务】,将主线程同步任务清空之后,检查微任务列表,如果不为空则逐个加入到主线程进行执行;微任务列表清空以后,执行宏任务,如果宏任务执行完一个以后微任务列表不为空了,那么接着执行微任务,直到微任务清空,再执行宏任务,这样一直循环到所有的任务列表清空,事件循环结束。
示例:
console.log(1); //1.同步任务#1
setTimeout(function(){ //1.宏任务加入marcotask $1
console.log(2);
new Promise(function(resolve){
console.log(3);
setTimeout(function(){ //$3
console.log(4);
});
resolve();
}).then(function(){ //&2
console.log(5);
});
});
new Promise(function(resolve){ //1.promise 是同步任务!!!#2
console.log(6);
setTimeout(function(){ //1.加入宏任务队列 $2
console.log(7);
});
resolve();
}).then(function(){ //1.微任务加入microtask &1
console.log(8);
});
console.log(9); //1.同步任务#3
结果为 1 6 9 8 2 3 5 7 4
- 首先执行同步任务
#1
,遇到setTimeout
宏任务$1
加入到macrotask
队列,遇到promise
为同步任务#2
,直接执行,promise
执行遇到setTimeout
宏任务$2
,加入macrotask
队列,遇到then()
&1
加入微任务队列,然后执行同步任务#3
,第一次事件循环结束;
此时:输出了结果 【1 6 9】
macrotask:$1
$2
microtask:&1
- 执行微任务
&1
,执行同步任务输出【8】,微队列清空,执行宏任务 - 执行宏任务
$1
,执行同步任务输出 【2】,遇到promise
执行其同步任务,输出【3】,遇到setTimeout
宏任务$3
,加入macrotask
,遇到then()
&2
,加入microtask
,$1
宏任务执行完毕,microtask
不为空,所以接下来执行微任务。 - 执行微任务列表的任务:
&2
,输出 【5】,结束后微队列清空,执行宏任务 - 执行
$2
宏任务,输出 【7】,执行结束 - 执行
$3
宏任务,输出 【4】,执行结束 - 此时所有队列清空,事件循环结束。
总结:先执行一个宏任务,再执行之下的所有微任务,如此循环;