2020-05-02

Javascript-运...

写在前面

  • 嗯,发现 运行机制 的面试题是真的多。。😓
  • 那这就是,最末篇吧
  • 加油 Elle 🤦‍♀️
  • 五一倒计时 2 天

实践记录

async / await

5.1 题目一

1
2
3
4
5
6
7
8
9
10
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
console.log('start')
  • 分析
    • 代码从上至下开始执行
    • 执行同步代码
    • async1();
      • console.log(“async1 start”);
      • await async2();
        • 等待执行 async2 结果
        • console.log(“async2”);
      • console.log(“async1 end”); 入队微任务队列
    • console.log(‘start’)
    • 当前宏任务中的所有同步代码执行完毕,开始执行当前宏任务中的微任务队列
    • console.log(“async1 end”);
  • 结论

    • 在这里,可以理解为「紧跟着await后面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中」
  • 等同于

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    async function async1() {
    console.log("async1 start");
    // 原来代码
    // await async2();
    // console.log("async1 end");

    // 转换后代码
    new Promise(resolve => {
    console.log("async2")
    resolve()
    }).then(res => console.log("async1 end"))
    }
    async function async2() {
    console.log("async2");
    }
    async1();
    console.log("start")
  • 运行结果
    • 题目一运行结果

5.2 题目二

1
2
3
4
5
6
7
8
9
10
11
12
13
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
setTimeout(() => {
console.log('timer')
}, 0)
console.log("async2");
}
async1();
console.log("start")
  • 分析

    • 代码从上至下开始执行
    • async1();
      • 执行同步代码
      • console.log(“async1 start”);
      • await async2();
        • setTimeout … 入队宏任务队列
        • console.log(“async2”);
      • console.log(“async1 end”); 入队微任务队列
    • console.log(“start”)
    • 当前宏任务中的同步代码执行完成,开始执行微任务队列
      • console.log(“async1 end”);
    • 第一个宏任务执行完成,开始执行下一个宏任务
    • console.log(‘timer’)
  • 运行结果

    • 题目二运行结果

5.3 题目三

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
setTimeout(() => {
console.log('timer1')
}, 0)
}
async function async2() {
setTimeout(() => {
console.log('timer2')
}, 0)
console.log("async2");
}
async1();
setTimeout(() => {
console.log('timer3')
}, 0)
console.log("start")
  • 分析

    • 代码从上至下开始执行
    • async1();
      • console.log(“async1 start”);
      • await async2();
        • setTimeout … 入队宏任务队列
        • console.log(“async2”);
      • console.log(“async1 end”); 入队微任务队列
      • setTimeout … 入队微任务队列
    • setTimeout … 入队宏任务队列
    • console.log(“start”)
    • 当前宏任务中的同步代码已经执行完成,开始执行微任务
    • console.log(“async1 end”);
    • setTimeout … 入队宏任务队列
    • 当前宏任务执行完成,开始执行下一个宏任务
    • console.log(‘timer2’);
    • 当前宏任务执行完成,开始执行下一个宏任务
    • console.log(‘timer3’)
    • 当前宏任务执行完成,开始执行下一个宏任务
    • console.log(‘timer1’)
  • 运行结果

    • 题目三运行结果

5.4 题目四

1
2
3
4
5
6
async function fn () {
// return await 1234
// 等同于
return 123
}
fn().then(res => console.log(res))
  • 分析

    • 代码从上至下开始执行
    • fn()
      • return 123
    • console.log(res) res –> 123
  • 运行结果

    • 题目四运行结果

5.5 题目五

1
2
3
4
5
6
7
8
9
10
11
async function async1 () {
console.log('async1 start');
await new Promise(resolve => {
console.log('promise1')
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
  • 分析

    • 代码从上至下开始执行
    • console.log(‘srcipt start’)
    • async1()
      • console.log(‘async1 start’);
      • new Promise … 等待执行
        • console.log(‘promise1’)
        • 由于 promise 并没有 resolved、rejected,所以一直处在 pending 状态,所以 会一直 await ,await 后的内容也包括 async1 后面的 then,都不会执行
    • console.log(‘srcipt end’)
  • 运行结果

    • 题目五运行结果

5.6 题目六

1
2
3
4
5
6
7
8
9
10
11
12
async function async1 () {
console.log('async1 start');
await new Promise(resolve => {
console.log('promise1')
resolve('promise1 resolve')
}).then(res => console.log(res))
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
  • 分析
    • 代码从上至下开始执行
    • console.log(‘srcipt start’)
    • async1()
      • console.log(‘async1 start’);
      • await new Promise … 等待 promise 执行
        • console.log(‘promise1’)
        • 其中 promise 状态变为 resolved,将其状态保存起来
        • new Promise then … 加入微任务队列
      • console.log(‘async1 success’); 入队微任务队列
      • return ‘async1 end’
    • async1().then … 入队微任务队列
    • console.log(‘srcipt end’)
    • 当前宏任务中的同步代码执行完成,开始执行微任务队列
    • .then(res => console.log(res))
      • res –> promise1 resolve
    • console.log(‘async1 success’);
    • res => console.log(res) res –> ‘async1 end’
  • 运行结果
    • 题目六运行结果

5.7 题目七

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function async1 () {
console.log('async1 start');
await new Promise(resolve => {
console.log('promise1')
resolve('promise resolve')
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => {
console.log(res)
})
new Promise(resolve => {
console.log('promise2')
setTimeout(() => {
console.log('timer')
})
})
  • 分析

    • 代码从上至下开始执行
    • console.log(‘srcipt start’)
    • async1()
      • console.log(‘async1 start’);
      • await new Promise …
        • console.log(‘promise1’)
        • 其中 promise 状态变为 resolve,将其保存起来
      • console.log(‘async1 success’); 入队微任务队列
      • return ‘async1 end’
    • async1().then … 入队微任务队列
    • new Promise,执行该构造函数代码
      • console.log(‘promise2’)
      • setTimeout … 加入宏任务队列
    • 当前宏任务中的同步代码执行完,开始执行微任务
    • console.log(‘async1 success’);
    • console.log(res) res –> ‘async1 end’
    • 当前宏任务执行完成,开始执行下一个宏任务
    • console.log(‘timer’)
  • 运行结果

    • 题目七运行结果

5.8 题目八

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}

async function async2() {
console.log("async2");
}

console.log("script start");

setTimeout(function() {
console.log("setTimeout");
}, 0);

async1();

new Promise(function(resolve) {
console.log("promise1");
resolve();
}).then(function() {
console.log("promise2");
});
console.log('script end')
  • 分析

    • 代码从上至下开始执行
    • console.log(“script start”);
    • setTimeout … 入队宏任务队列
    • async1()
      • console.log(“async1 start”);
      • await async2();
        • console.log(“async2”);
      • console.log(“async1 end”); 入队微任务队列
    • new promise,执行该构造函数
      • console.log(“promise1”);
      • 其中 promise 状态改变为 resolve,将其保存
    • new promise then … 入队微任务队列
    • console.log(‘script end’)
    • 当前宏任务中的同步代码执行完成,开始执行微任务
    • console.log(“async1 end”);
    • new Promise then …
      • console.log(“promise2”);
    • 当前宏任务执行完成,开始执行下一个宏任务
      • console.log(“setTimeout”);
  • 运行结果

    • 题目八运行结果

5.9 题目九

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
async function testSometing() {
console.log("执行testSometing");
return "testSometing";
}

async function testAsync() {
console.log("执行testAsync");
return Promise.resolve("hello async");
}

async function test() {
console.log("test start...");
const v1 = await testSometing();
console.log(v1);
const v2 = await testAsync();
console.log(v2);
console.log(v1, v2);
}

test();

var promise = new Promise(resolve => {
console.log("promise start...");
resolve("promise");
});
promise.then(val => console.log(val));

console.log("test end...");
  • 分析
    • 代码从上至下开始执行
    • test()
      • console.log(“test start…”);
      • await testSometing()
        • console.log(“执行testSometing”);
        • “testSometing”;
        • const v1 = “testSometing”;
      • console.log(“testSometing”); 入队微任务队列
      • await testAsync() 入队微任务队列
      • console.log(v2); 入队微任务队列
      • console.log(v1, v2); 入队微任务队列
      • new Promise, 执行该构造函数代码
        • console.log(“promise start…”);
        • 其 promise 状态变为 resolve,将其保存
      • promise.then … 入队微任务队列
      • console.log(“test end…”);
      • 当前宏任务中的同步代码执行完成,开始执行微任务
        • console.log(“testSometing”); v1 –> “testSometing”
        • await testAsync()
          • console.log(“执行testAsync”);
          • return Promise.resolve(“hello async”);
          • Promise.resolve(“hello async”) … 入队微任务队列
        • console.log(v2); … 重新入队微任务队列
        • console.log(v1, v2); 重新入队微任务队列
        • promise.then …
          • console.log(val) val –> promise
        • Promise.resolve(“hello async”) return “hello async”
        • v2 = “hello async”
        • console.log(v2);
        • console.log(“hello async”);
        • console.log(v1, v2);
          • console.log(“执行testAsync”, “hello async”);
  • 运行结果
    • 题目九运行结果

async 处理错误

6.1 题目一

1
2
3
4
5
6
7
8
9
10
11
12
async function async1 () {
await async2();
console.log('async1');
return 'async1 success'
}
async function async2 () {
return new Promise((resolve, reject) => {
console.log('async2')
reject('error')
})
}
async1().then(res => console.log(res))
  • 分析

    • 代码从上至下开始执行
    • async1()
      • await async2();
        • new Promise,执行该构造函数
          • console.log(‘async2’)
          • reject(‘error’) promise 状态变为 reject,将其保存起来,并向上抛
          • Promise {: “error”}
  • 运行结果

    • 题目一运行结果

6.2 题目二

1
2
3
4
5
6
7
8
9
10
11
async function async1 () {
try {
await Promise.reject('error!!!')
} catch(e) {
console.log(e)
}
console.log('async1');
return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')
  • 分析

    • 代码从上至下开始执行
    • async1()
      • Promise.reject(‘error!!!’)
        • promise 状态变为 reject,将其状态保存起来,向上抛错
      • catch 错误 入队微任务队列
      • console.log(‘async1’); 入队微任务队列
      • return Promise.resolve(‘async1 success’)
    • async1().then … 入队微任务队列
    • console.log(‘script start’)
    • 当前宏任务中的同步代码执行完成,开始执行微任务
    • console.log(e) e –> ‘error!!!’
    • console.log(‘async1’);
    • console.log(res) res –> ‘async1 success’
  • 运行结果

    • 题目二运行结果

综合题

7.1 题目一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
console.log(p)
}, 0)
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);
  • 分析

    • 代码从上至下开始执行
    • first()
      • return 一个 promise 对象。先执行 new Promise,该构造函数代码
      • console.log(3);
      • resolve(2)
        • 该 promise 状态变为 resolved,保存起来
      • p new Promise, 执行该构造函数代码
        • console.log(7);
        • setTimeout … 入队宏任务队列
        • resolve(1);
        • 该 promise 状态变为 resolve ,保存起来
      • p.then … 入队微任务队列
    • first().then … 入队微任务队列
    • 执行同步代码
      • console.log(4);
    • 该宏任务中的同步代码都执行完成,开始执行微任务队列
    • p.then
    • console.log(arg) arg –> 1
    • p.then 入队微任务队列
    • console.log(arg) arg –> 2
    • 当前宏任务中的代码都执行完成,开始执行下一个宏任务
    • setTimeout …
      • console.log(5);
      • resolve(6); promise p 的状态已经改变,无法再进行更改,所以该行代码,无效
      • console.log(p) p –> 1
  • 运行结果

    • 题目一运行结果

7.2 题目二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const async1 = async () => {
console.log('async1');
setTimeout(() => {
console.log('timer1')
}, 2000)
await new Promise(resolve => {
console.log('promise1')
})
console.log('async1 end')
return 'async1 success'
}
console.log('script start');
async1().then(res => console.log(res));
console.log('script end');
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.catch(4)
.then(res => console.log(res))
setTimeout(() => {
console.log('timer2')
}, 1000)
  • 分析

    • 代码从上至下开始执行
    • console.log(‘script start’);
    • async1()
      • console.log(‘async1’);
      • setTimeout … 2000入队宏任务队列
      • await new Promise
        • 执行该构造函数代码
          • console.log(‘promise1’)
      • 由于该 promise 没有被 resolve、reject,一直处在 pending,因此 后面的
        • console.log(‘async1 end’) 入队微任务队列
        • return ‘async1 success’
        • 均不执行
    • async1().then … 入队微任务队列
    • console.log(‘script end’);
    • Promise.resolve(1)
      • 该 promie 状态变为1,保存起来
    • Promise.resolve(1).then … 入队微任务队列
    • setTimeout … 1000入队宏任务队列
    • 当前宏任务中的同步代码执行完成,开始执行微任务
    • Promise.resolve(1).then …
      • 由于 .then(2) .then(Promise.resolve(3)) .catch(4) 一个是数字,一个是对象,会发生值透传现象
      • console.log(res) res –> 1
    • 当前宏任务执行完成,开始执行下一个宏任务
    • setTimeout … 1000
      • console.log(‘timer2’)
    • setTimeout … 2000
        • console.log(‘timer1’)
  • 运行结果

    • 题目二运行结果

7.3 题目三

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve('resolve3');
console.log('timer1')
}, 0)
resolve('resovle1');
resolve('resolve2');
}).then(res => {
console.log(res)
setTimeout(() => {
console.log(p1)
}, 1000)
}).finally(res => {
console.log('finally', res)
})
  • 分析

    • 代码从上至下开始执行
    • new Promise,执行该构造函数代码
      • setTimeout … 入队宏任务队列
      • resolve(‘resovle1’);
      • promise 状态变为 resolve1,并保存起来
    • Promise.then …
      • console.log(res) res –> ‘resovle1’
      • setTimeout … 入队宏任务队列
    • Promise.finally …
      • console.log(‘finally’, res) res –> undefined
    • 当前宏任务中的代码全部执行完成,开始执行下一个宏任务
    • resolve(‘resolve3’); promise 状态一旦改变,无法更改
    • console.log(‘timer1’)
    • 当前宏任务中的代码全部执行完成,开始执行下一个宏任务
    • console.log(p1) p1 –> .finally的返回值 –> undefined
  • 结论

    • finally 不管 Promise 的状态是 resolved 还是 rejected 都会执行,且它的回调函数是接收不到 Promise 的结果的,所以 finally() 中的 res 是一个迷惑项
  • 运行结果

    • 题目三运行结果

参考文献

写在后面

  • 祝大家多多发财