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
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
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')
})
})
自己猜下答案吧!