0%

0x01 Promise理解第一个部分

基础铺垫

event loop 它的执行顺序:

  • 一开始整个脚本作为一个宏任务执行
  • 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  • 当红任务执行完出队,检查微任务列表,有则一次执行,直到全部执行完
  • 执行浏览器 UI 线程渲染工作
  • 检查是否有Web Worker任务,有则执行
  • 执行完本轮的宏任务,回到 2,依次循环,直到宏任务和微任务队列都为空

微任务:

  • MutationObserver、Promise.then()或 reject()、Promise 为基础开发的其它技术,比如 feth Api v8 的垃圾回收过程、Node 独有的 process.nextTick。

宏任务:

  • script、setTimeout、setInterval、setImmdiate、I/O、UI rendering

注意:所有任务开始的时候,由于宏任务包括了 script,所以浏览器会先执行一个宏任务,在这个过程中你看到的延迟任务(例如:setTimeout)将放到下一轮宏任务中执行。

只看 Promise 部分(理解相对如容易)

题目一:

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

过程分析:

  • 从上至下,先遇到new Promise,执行该构造函数中的代码promise1
  • 然后执行同步代码1 ,此时promise1没有被resolve或者reject,因此状态还是pending

结果:

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

题目二:

1
const promise = new Promise((resolve, reject) => {
2
  console.log(1);
3
  resolve("success");
4
  console.log(2);
5
});
6
7
promise.then(() => {
8
  console.log(3);
9
});
10
11
console.log(4);

过程分析:

  • 从上至下,先遇到new Promise,执行中的同步代码1
  • 再遇到resolve('success'),将promise的状态改为了resolved并且将值保存下来
  • 继续执行同步代码2
  • 跳出promise,往下执行,碰到promise.then这个微任务,将其加入微任务队列
  • 同步执行代码4
  • 本轮宏任务全部执行完毕,检查微任务队列,发现promise.then这个微任务则状态为resolved,执行它。

结果:

1
1 2 4 3

题目三:

1
const promise = new Pormise((resolve, reject) => {
2
  console.log(1);
3
  console.log(2);
4
});
5
6
promise.then(() => {
7
  console.log(3);
8
});
9
10
console.log(4);

过程分析:

  • 和题目二相似,只不过在promise中并没有resolve或者reject
  • 因此promise.then并不会执行,它只有在被改变了状态之后才会执行。

结果:

1
1 2 4

题目四:

1
const pormise1 = new Promise((resolve, reject) => {
2
  console.log("promise1");
3
  resolve("resolve1");
4
});
5
6
const promise2 = promise1.then(res => {
7
  console.log(res);
8
});
9
10
console.log("1", promise1);
11
console.log("2", promise2);

过程分析:

  • 从上至下,先遇到new Promise,执行该构造函数中的代码promise1
  • 碰到resolve函数,将promise1的状态改变为resolved,并将结果保存下来
  • 碰到promise1.then这个微任务,将它放入微任务队列
  • promise2是一个新状态为pendingPromise
  • 执行同步代码1,同时打印出promise1的状态是resolved
  • 执行同步代码2,同时打印出promise2的状态是pending
  • 宏任务执行完毕,查找微任务队列,发现promise1.then这个微任务且状态为resolved,执行它。

结果:

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

题目五:

1
const fn = () =>
2
  new Promise((resolve, reject) => {
3
    console.log(1);
4
    resolve("success");
5
  });
6
7
fn().then(res => {
8
  console.log(res);
9
});
10
11
console.log("start");

过程分析:

  • 从上至下,new Promise构造函数创建一个实例赋值给fn
  • fn()调用执行1
  • fn().then新的微任务,加入微任务队列中去
  • 执行同步代码start
  • 因为前面resolve,进入微任务队列执行,输出success

结果:

1
'1'
2
'start'
3
'success'

题目六:

fn调用放start之后

1
const fn = () =>
2
  new Promise((resolve, reject) => {
3
    console.log(1);
4
    resolve("success");
5
  });
6
7
console.log("start");
8
9
fn().then(res => {
10
  console.log(res);
11
});

过程分析:

  • new Promise赋值给fnfn未发生调用
  • 执行同步代码start
  • fn发生调用,执行1,之后执行resolve
  • fn().then()随后执行,输出success

结果:

1
'start'
2
'1'
3
'success'

Promise 结合 setTimeout

题目一:

1
console.log("start");
2
setTimeout(() => {
3
  console.log("time");
4
});
5
Promise.resolve().then(() => {
6
  console.log("resolve");
7
});
8
console.log("end");

过程分析:

  • 刚开始整个脚本为一个宏任务执行,对于同步代码直接进入执行栈进行执行,因此先打印出startend
  • setTimeout作为一个宏任务,放入宏任务队列(下一个循环)
  • Promise.then作为一个微任务,放入微任务队列
  • 本次宏任务执行完毕,检查微任务,发现Promise.then,然后执行
  • 接下来进入下一个宏任务,发现setTimeout,然后执行

结果:

1
'start'
2
'end'
3
'resolve'
4
'time'

题目二:

1
const promise = new Promise((resolve, reject) => {
2
  console.log("1");
3
  setTimeout(() => {
4
    console.log("timerStart");
5
    resolve("success");
6
    console.log("timerEnd");
7
  }, 0);
8
  console.log(2);
9
});
10
11
promise.then(res => {
12
  console.log(res);
13
});
14
15
console.log(4);

过程分析:

  • 从上至下,先遇到new Promise,执行该构造函数中的代码1
  • 然后碰到了定时器,将这个定时器中的函数放到下一个宏任务的延迟队列中等待执行
  • 执行同步代码2
  • 跳出promise函数,遇到promise.then,但其状态还是为pending,这里理解为先不执行
  • 执行同步代码4
  • 一轮循环过后,进入第二次宏任务,发现延迟队列中有setTimeout定时器,执行它
  • 首先执行timeStart,然后遇到了resolve,将promise的状态改为resolved且保存结果并将之前的promise.then推入微任务队列
  • 继续执行同步代码timerEnd
  • 宏任务全部执行完毕,查找微任务队列,发现promise.then这个微任务,执行它。

结果:

1
'1'
2
'2'
3
'4'
4
'timerStart'
5
'timerEnd'
6
'success'

题目三:

1
setTimeout(() => {
2
  console.log("timer1");
3
  setTimeout(() => {
4
    console.log("timer3");
5
  }, 0);
6
});
7
8
setTimeout(() => {
9
  console.log("timer2");
10
}, 0);
11
12
console.log("start");

过程分析:

  • script本来是一个宏任务,先执行start
  • setTimeout(timer1)、setTimeout(timer2)宏任务放入宏任务队列
  • 检测宏任务队列,执行timer1之后将setTimeout(timer3)加入宏任务队列,然后执行timer2
  • 检测宏任务队列,执行timer3

结果:

1
'start'
2
'timer1'
3
'timer2'
4
'timer3'

题目四:

1
setTimeout(() => {
2
  console.log("timer1");
3
  Promise.resolve().then(() => {
4
    console.log("promise");
5
  });
6
}, 0);
7
8
setTimeout(() => {
9
  console.log("timer2");
10
}, 0);
11
12
console.log("start");

过程分析:

  • script本身是宏任务,优先执行start
  • setTimeout(timer1)setTimeout(timer2)放入宏任务队列,并开始执行
  • 执行宏任务timer1,产生微任务Promise.resolve().then,微任务进入队列执行promise
  • 执行宏任务timer2

题目五:

1
Promise.resolve().then(() => {
2
  console.log("promise1");
3
  const timer2 = setTimeout(() => {
4
    console.log("timer2");
5
  }, 0);
6
});
7
8
const timer1 = setTimeout(() => {
9
  console.log("timer1");
10
  Promise.resolve().then(() => {
11
    console.log("promise2");
12
  });
13
}, 0);
14
15
console.log("start");

过程分析:

  • script作为第一个宏任务来执行,这里标记为宏任务 1
  • 遇到Promise.resolve().then这个微任务,将then中的内容加入第一次的微任务队列,标为微任务 1
  • 遇到setTimeout(timer1),加入到宏任务队列,标为宏任务 2,等待执行
  • 执行宏任务 1的同步代码start,完成后,检查微任务队列
  • 发现有一个promise.then这个微任务执行,打印同步代码promise1,发现定时器timer2,将它加入宏任务 2的后面,标记为宏任务 3
  • 第一次微任务队列(微任务 1)执行完毕,执行第二次宏任务(宏任务 2),首先执行同步代码timer1
  • 然后遇到了promise2这个微任务,将它加入此次循环的微任务队列,标为微任务 2
  • 宏任务 2中没有同步代码可执行,查找本次循环的微任务队列(微任务 2),发现promise2
  • 第二轮执行完毕,执行宏任务 3,打印出timer2

结果:

1
'start'
2
'promise1'
3
'timer1'
4
'promise2'
5
'timer2'

题目六:

1
const promise1 = new Promise((resolve, reject) => {
2
  setTimeout(() => {
3
    reslove("success");
4
  }, 0);
5
});
6
7
const promise2 = pormise1.then(() => {
8
  throw new Error("error!!!");
9
});
10
11
console.log("promise1", promise1);
12
console.log("promise2", promise2);
13
14
setTimeout(() => {
15
  console.log("promise1", promise1);
16
  console.log("promise2", promise2);
17
}, 2000);

过程分析:

  • 先执行第一个new Promise中的函数,碰到setTimeout将它加入下一个宏任务列表
  • 跳出new Promise,碰到promise1.then这个微任务,但其状态还是为pending,这里理解为先不执行
  • promise2是一个新的状态为pendingPromise
  • 执行同步代码console.log('promise1'),则打印出的promise1的状态为pending
  • 执行同步代码console.log('promise2'),则打印出的promise2的状态为pending
  • 碰到第二个setTimeout定时器,将其放入下一个宏任务列表
  • 第一轮宏任务执行结束,并且没有微任务需要执行,因此执行第二轮任务
  • 先执行第一个定时器的内容,将promise1的状态改为resolved且保存结果并将之前的promise1.then推入微任务队列
  • 该定时器中没有其他的同步代码可执行,因此执行本轮的微任务队列,也就是promise1.then,它将抛出一个错误,且将promise2的状态设置为了rejected
  • 第一个定时器执行完毕,开始执行第二个定时器中的内容
  • 打印出promise1,且此时promise1的状态为resolved
  • 打印出promise2,且此时promise2的状态为rejected

结果:

1
'promise1' Promise{<pending>}
2
'promise2' Promise{<pending>}
3
Uncaught (in promise) Error: error!!!
4
'promise1' Promise{<resolved>: "success"}
5
'promise2' Promise{<rejected>: Error: error!!!}

题目七:

1
const promise1 = new Promise((resolve, reject) => {
2
  setTimeout(() => {
3
    resolve("success");
4
    console.log("timer1");
5
  }, 1000);
6
  console.log("promise1里面的内容");
7
});
8
9
const promise2 = promise1.then(() => {
10
  throw new Error("error!!!");
11
});
12
13
console.log("promise1", promise1);
14
console.log("promise2", promise2);
15
16
setTimeout(() => {
17
  console.log("timer2");
18
  console.log("promise1", promise1);
19
  console.log("promise2", promise2);
20
}, 2000);

结果:

1
'promise1里面的内容'
2
'promise1' Promise{<pending>}
3
'promise2' Promise{<pending>}
4
'timer1'
5
Uncaught (in promise) Error: error!!!
6
'timer2'
7
'promise1' Promise{<resolved>: success>}
8
'promise2' Promise{<reject>: error!!!>}