1.任务队列
JavaScript
语言是单线程,就意味着所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。所有任务可以分为两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务
指的是:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。异步任务
指的是,不进入主线程、而进入任务队列(task queue)
的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
所有同步任务都在主线程上执行,形成一个
执行栈
(execution context stack)。主线程之外,还存在一个
"任务队列"
(task queue)。只要异步任务有了运行结果,就在"任务队列"
之中放置一个事件。一旦
"执行栈"
中的所有同步任务执行完毕,系统就会读取"任务队列"
,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈
,开始执行。主线程不断重复上面的第三步。
注:
setTimeout(fn,0)
的含义是,指定某个任务在主线程
最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"
的尾部添加一个事件,因此要等到同步
任务和"任务队列"
现有的事件都处理完,才会得到执行。
任务队列
中任务又分为:宏任务和微任务
宏任务
:整体代码script
、setTimeout、setInterval微任务
:Promise、process.nextTick任务队列执行过程:先执行一个宏任务、执行过程中遇到宏任务或微任务,将他们推入到响应的任务队列中、之后在执行微任务、宏任务。如此循环称为事件循环。
2. 事件和回调函数
任务队列
是一个事件队列(消息队列),主线程读取任务队列,就是读取里面有哪些事件。回到函数
:指的是会被主线程挂起来的代码,异步任务必须指定回调函数,当主线程开始执行异步任务
,就是执行对应的回调函数。任务队列
是一个先进先出的数据结构,在前面的事件,优先被主线程读取。但是由于定时器功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。
3. 通用公式和demo
同步>异步>回调
1.
for (var i = 0; i < 9; i++) {
setTimeout( function(){
console.log(i);
}
,1000)
}
console.log(i);
for
循环先执行,每个i
都将setTimeOut
回调扔到消息队列中- 接着执行后面的同步
console.log
,此时i=9
,到此同步执行完毕,回消息队列执行回调
9个setTimeOut
依次执行,
执行结果:999999999
2.
let a = new Promise(function (resolve, reject) {
console.log(1);
setTimeout(function () {
console.log(2)
},0)
resolve(true);
}).then(function () {
console.log(3);
});
console.log(4);
// 1432
-
a变量是一个
Promise
,我们知道Promise是异步的
,是指它的then()
和catch()
方法,Promise本身
还是同步的,所以这里先执行a变量
内部的Promise
同步代码。(同步优先)所以1先出现。 -
遇到
setTimeOut
回调放到消息队列中,resolve(true)
,调用.then
中函数放到消息队列中,最后的console.log
是同步操作,所以此时打印4,同步这就执行完了。 -
异步从消息队列里出来也就是执行
.then()
,打印3,最后回调
也从消息队列出来,打印2。
执行结果:1432
3.
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("1111")
}, 1000)
resolve()
}).then(() => {
console.log('Hello')
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2222')
}, 1000)
resolve()
}).then(() => {
console.log('hello vue')
})
})
new Promise((resolve, reject) =>{
setTimeout(() => {
console.log('3333')
}, 1)
resolve('Hello World!')
} ).then((data) => {
console.log(data)
})
console.log('我是同步')
上面这个例子:我们按照写的顺序把上面三个setTimeOut
分别叫做S1
,S2
和S3
, 三个,then
叫做T1
, T2
, T3
。
- 遇到
S1
放到消息对列中,resolve
函数调用T1
放到消息队列中,遇到S3
,放到消息队列中,resolve
函数调用T3
放到消息队列中。 - 遇到同步
console.log
打印我是同步,同步执行完毕。 - 回到消息队列,此时消息队列中有
S1, S3, T1, T3
, 按照之前提到的setTimeOut
会在任务队列队尾执行,所以先执行T1
, 打印Hello - 遇到
S2
放到任务队列中,遇到resolve
调用T2
放到消息队列中。执行T3
打印Hello World - 执行
T2
,打印hello vue。 - 现在可以执行
setTimeOut
了,S3
延迟1ms
执行,所以S3
的打印结果3333,然后是S1
打印1111,S2
打印2222。
执行结果是:
我是同步
Hello
Hello World!
hello vue
3333
1111
2222
4. 经典题……
console.log(1);
setTimeout(function () {
console.log('2');
process.nextTick(function () {
console.log('3');
})
new Promise(function (resolve) {
console.log('4');
resolve();
}).then(function () {
console.log('5')
})
})
process.nextTick(function () {
console.log('6');
})
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8')
})
setTimeout(function () {
console.log('9');
process.nextTick(function () {
console.log('10');
})
new Promise(function (resolve) {
console.log('11');
resolve();
}).then(function () {
console.log('12')
})
})
自己猜下答案吧!