事件循环(event loop)

事件循环的过程

  1. 同步代码先进入执行栈(JavaScript是单线程,执行栈即主线程)执行,遇到异步代码,则放入宏任务队列或微任务队列
  2. 同步代码执行完,则微任务队列中的代码按先入先出的顺序从任务队列出来,进入执行栈,成为新的同步代码被执行
  3. 微任务队列为空,则将宏任务队列的任务放入执行栈执行,当宏任务队列为空时,回到执行栈执行同步代码,回到第1步。

被放到执行栈中的代码都会以同步代码执行

异步任务分为宏任务和微任务,同步代码先于异步代码执行,微任务先于宏任务。
微任务包括:

1. MutationObserver、
2. Promise.then()或catch()、
3. Promise为基础开发的其它技术,比如fetch API、V8的垃圾回收过程、
4. Node独有的process.nextTick。

宏任务包括:

1. script(整体代码)
2. setTimeout
3. setInterval
4. I/O
5. UI交互事件
6. postMessage
7. MessageChannel
8. setImmediate(Node.js 环境)

基础题

题目1

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
})
console.log('1', promise1);

输出

'promise1'
'1' Promise{<pending>}

分析:

  1. 执行同步代码
    • 给promise1赋值
    • 执行console.log('promise1')
    • 执行console.log('1', promise1);,此时promise1没有被resolve或者reject,因此状态还是pending
  2. 因为resolve和reject才是异步函数,但这里并没有调用并使用then赋值,所以异步任务队列均为空

题目2

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve('success')
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

输出:
···
1
2
4
3
···
分析:

  1. 执行执行栈中的同步代码
    • promise赋值,执行Promise中的同步代码,打印1,调用resolve回调函数,因为是异步函数且为微任务所以放入了微任务队列,打印2
    • promise.then给相应的resolve赋值,以箭头函数为值
    • 执行同步代码,打印4
  2. 检查微任务队列,执行resolveconsole.log(3)被放入执行栈执行

题目3

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

输出:

1
2
4

分析:同题目2,但是因为promise的resolve函数没有被调用,所以即使赋予了箭头函数也不会执行,于是就不会打印3。
promise.then只有在被改变了状态之后才会执行。

题目4

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

分析:

'promise1'
'1' Promise{<resolved>: 'resolve1'}
'2' Promise{<pending>}
'resolve1'

分析:

  1. 执行同步代码:
    • 给promise1赋值,执行其中的代码,打印“promise1”,调用resolve改变promise1的状态
    • 给promise2赋值,这里“Promise.prototype.then() 方法返回一个新的期约实例”,因为promise2是基于promise1创建的,但在promise1的处理函数被放入执行栈执行之前,promise2的状态仍是pending。
    • promise1的处理函数进入微任务队列
    • 打印1和promise1的状态
    • 打印2和promise2的状态(现在promise1的处理函数尚未执行,所以是pending)
  2. 检查微任务队列,执行promise1的resolve,打印传入的"res",即打印“resolve1”

改一改代码:

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
console.log('1', promise1);
const promise2 = promise1.then(res => {
  console.log('ahead', promise2);
  console.log("res",res);
})
//promise2.then(res=>console.log("promise2",res),rej=>console.log("err"));
console.log('1', promise1);
console.log('2', promise2);
setTimeout(console.log,0,'2',promise2);

输出:

promise1
1 Promise { <state>: "fulfilled", <value>: "resolve1" }
1 Promise { <state>: "fulfilled", <value>: "resolve1" }
2 Promise { <state>: "pending" }
ahead Promise { <state>: "pending" }
res resolve1
2 Promise { <state>: "fulfilled", <value>: undefined }

最终的promise2是处于fulfilled状态的

关于值传递的:

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log("res",res);
  return "resolve2";
})
promise2.then(res=>console.log("promise2",res),rej=>console.log("err"));
console.log('1', promise1);
console.log('2', promise2);
setTimeout(console.log,0,'2',promise2);

输出:

promise1
1 Promise { <state>: "fulfilled", <value>: "resolve1" }
2 Promise { <state>: "pending" }
res resolve1
promise2 resolve2
2 Promise { <state>: "fulfilled", <value>: "resolve2" }

对比上面两段代码可以发现,当promise1的resolve没有显式返回一个值时,以const promise2 = promise1.then创建的promise2获得的值是undefined。而当上一个期约返回一个值时,该值将被promise2传给相应的处理函数。

题目5

const fn = () => (new Promise((resolve, reject) => {
  console.log(1);
  resolve('success')
}))
fn().then(res => {
  console.log(res)
})
console.log('start')

输出:

1
start
success

分析:
fn返回的是一个解决期约(fulfilled)。

题目6

const fn = () =>
  new Promise((resolve, reject) => {
    console.log(1);
    resolve("success");
  });
console.log("start");
fn().then(res => {
  console.log(res);
});

输出:

start
1
success

分析:同步代码前面的先执行。

感谢阅读。

百炼成钢!!!

参考:

【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理)
《JavaScript高级程序设计》(第四版)