首先来一段代码开篇javascript
console.log(1);
setTimeout(function() {
console.log(2);
});
function fn() {
console.log(3);
setTimeout(function() {
console.log(4);
}, 2000);
}
new Promise(function(resolve, reject){
console.log(5);
resolve();
console.log(6);
}).then(function() {
console.log(7);
})
fn();
console.log(8);
复制代码
思考一下,能给出准确的输出顺序吗?java
下面一步步的了解,最后看看这块代码怎么去执行的。node
进程: 运行的程序就是一个进程,好比你正在运行的浏览器,它会有一个进程。git
线程: 程序中独立运行的代码段。github
一个进程由单个或多个线程组成,线程是负责执行代码的。ajax
学过JS的想必都知道JS是单线程的,那么既然有单线程就有多线程,下面首先看看单线程与多线程的区别。api
单线程 从头执行到尾,一行一行执行,若是其中一行代码报错,那么剩下代码将再也不执行。同时容易代码阻塞。promise
多线程 代码运行的环境不一样,各线程独立,互不影响,避免阻塞。浏览器
js既然是单线程,那么确定是排队执行代码,那么怎么去排这个队,就是Event Loop。虽然JS是单线程,但浏览器不是单线程。浏览器中分为如下几个线程:bash
其中JS线程和UI线程相互互斥,也就是说,当UI线程在渲染的时候,JS线程会挂起,等待UI线程完成,再执行JS线程
JS会存在执行栈,从上至下执行js代码,当遇到异步api时,列如上面所述的各类非JS线程的事件,那么会扔给对应的线程去处理,等处理完毕后,则把回调函数放入事件队列中,等待执行栈执行完毕,再去读取事件队列中的回调函数执行。
整个过程,执行栈,读取事件队列就是Event Loop
再来看看promise, 若是对promise不是很了解的同窗能够看看另外一篇我写的文章Promise是个什么鬼?实现一个Promise.
Promise在整个执行中是个特殊的存在,传入Promise的fn是在当前执行栈中的,会当即执行,但它的then方法是在执行栈以后,事件队列以前,固然这个和浏览器实现有关,大部分浏览器是微任务(Microtask),也有浏览器放入了宏任务(Macrotask),chorme大哥是放入了微任务,其余纷纷效仿。那你们可能会问什么是微任务?什么是宏任务了?
Nodejs是经过V8引擎去解析的,解析后的代码会去调用node提供的api执行,这些API由libuv这个库去分配线程执行,最后异步返回给V8引擎。
在Node中提供了2个方法和咱们的执行队列有关
把方法放入执行栈的底部,并不放入宏任务和微任务
cosnole.log(1);
process.nextTick(function(){
console.log(2);
});
new Promise(function() {
console.log(3);
}).then(function() {
console.log(4);
})
console.log(5);
复制代码
由于nextTick是放入了执行栈的底部,那么会优先于Promise的then方法,故输出为1 3 5 2 4
把方法放入宏任务的队列中去,但有一个奇怪的事发生,看下面代码:
setImmediate(function() {
console,log(1);
});
setTimeout(function() {
console.log(2);
}, 0);
复制代码
你们能够试试把代码屡次执行,发现输出顺序不必定,他们都是放入了宏任务中,但在node文档中,setImmediate老是排在setTimeout前面,可是在实际中确不必定,不知道是否是一个bug。
setTimeout(function(){
console.log(1);
}, 2000);
task();
复制代码
假设task函数执行须要5秒钟,那么打印1须要在5秒以后再打印,task占用了当前执行栈,要等执行栈执行完毕后再去读取微任务,等微任务完成,这个时候才会去读取宏任务里面的setTimeout回调函数执行。setInterval同理,例如每3秒放入宏任务,也要等到执行栈的完成。
setTimeout(function() {
console.log(1);
},0);
复制代码
可是根据标准这个时候最低是4毫秒,即使如今执行栈已经完成。0是不成立的。写0浏览器为默认为最低毫秒数。
如今再回到上面的代码,有答案了吗?
// 非异步api,当即执行
console.log(1);
// 放入全局宏任务
setTimeout(function() {
console.log(2);
});
// 声明函数,但暂时未调用,不会立马造成执行栈
function fn() {
// 调用fn时当即执行
console.log(3);
// 放入当前fn执行栈宏任务
setTimeout(function() {
console.log(4);
}, 2000);
}
new Promise(function(resolve, reject){
// task任务当即执行
console.log(5);
resolve();
console.log(6);
}).then(function() {
// then方法放入微任务
console.log(7);
})
// 调用fn进入下个执行栈
fn();
// fn执行栈完成执行
console.log(8);
复制代码
答案就是 1 5 6 3 8 7 2 4