本篇博文是学习的掘金的一篇博文:【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理),经过自己一个个问题的学习,整理如下:
一.Promise的几道基础题
题目一 ✨
1 2 3 4 5 6
| const promise1 = new Promise((resolve,reject)=>{ console.log('promise1') }) console.log('1',promise1)
|
先执行构造函数中的代码promise1,然后执行同步代码 1,由于没有resolve或者reject,此时状态为pending
题目二 ✨✨
1 2 3 4 5 6 7 8 9 10
| const promise = new Promise((resolve,reject)=>{ console.log(1) resolve('success') console.log(2) }) promise.then(()=>{ console.log(3) }) console.log(4)
|
- 首先执行同步代码,打印出1
- 接着resolve的出现,只是将promise的状态改变成了resolved,将success这个值保存了下来。
- 会接着执行同步代码,输出2
- promsie.then是一个微任务,放入到微任务列表,等待宏任务执行完毕后,来执行微任务列表
- 继续执行本轮的宏任务,输出4
- 之后本轮宏任务执行完毕,检查微任务列表发现了这个promise…then,执行输出3
题目三 ✨
1 2 3 4 5 6 7 8 9
| const promise = new Promise((resolve, reject) => { console.log(1); console.log(2); }); promise.then(() => { console.log(3); }); console.log(4);
|
在promise中没有resolve或者reject,因此promise.then不会执行
题目四 ✨
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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);
|
题目五 ✨
1 2 3 4 5 6 7 8 9 10 11
| const fn = () => (new Promise((resolve, reject) => { console.log(1); resolve('success') })) fn().then(res => { console.log(res) }) console.log('start')
|
fn是一个函数且直接返回的是一个Promise,主要是因为fn的调用在start之前的,所以fn里面的代码先执行
题目六 ✨
1 2 3 4 5 6 7 8 9 10 11 12
| const fn = () => new Promise((resolve, reject) => { console.log(1); resolve("success"); }); console.log("start"); fn().then(res => { console.log(res); });
|
这个fn的调用是在start之后才开始执行的,所以先打印start,再去执行fn这个函数
二、Promise结合setTimeout
题目七 ✨
1 2 3 4 5 6 7 8 9 10 11 12
| console.log('start') setTimeout(() => { console.log('time') }) Promise.resolve().then(() => { console.log('resolve') }) console.log('end')
|
题目八 ✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const promise = new Promise((resolve, reject) => { console.log(1); setTimeout(() => { console.log("timerStart"); resolve("success"); console.log("timerEnd"); }, 0); console.log(2); }); promise.then((res) => { console.log(res); 宏任务 }); console.log(4);
|
题目九 ✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| setTimeout(() => { console.log('timer1'); setTimeout(() => { console.log('timer3') }, 0) }, 0) setTimeout(() => { console.log('timer2') }, 0) console.log('start')
|
加个Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| setTimeout(() => { console.log('timer1'); Promise.resolve().then(() => { console.log('promise') }) }, 0) setTimeout(() => { console.log('timer2') }, 0) console.log('start')
|
之所以先打印出promise是因为,第一个setTimeout为下一个宏任务队列,第二个setImeout为下下一个宏任务队列,因此在第二个宏任务队列执行完毕之后,会先去本轮的微任务队列中去查找是否有微任务。
题目十 ✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Promise.resolve().then(() => { console.log('promise1'); const timer2 = setTimeout(() => { console.log('timer2') }, 0) }); const timer1 = setTimeout(() => { console.log('timer1') Promise.resolve().then(() => { console.log('promise2') }) }, 0) console.log('start');
|
题目十一 ✨✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 1000) }) const promise2 = promise1.then(() => { throw new Error('error!!!') }) console.log('promise1', promise1) console.log('promise2', promise2) setTimeout(() => { console.log('promise1', promise1) console.log('promise2', promise2) }, 2000)
|
题目十二 ✨✨
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
| const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("success"); console.log("timer1"); }, 1000); console.log("promise1里的内容"); }); const promise2 = promise1.then(() => { throw new Error("error!!!"); }); console.log("promise1", promise1); console.log("promise2", promise2); setTimeout(() => { console.log("timer2"); console.log("promise1", promise1); console.log("promise2", promise2); }, 2000);
|
三、Promise中的then、catch、finally
题目十三 ✨
1 2 3 4 5 6 7 8 9 10 11 12
| const promise = new Promise((resolve, reject) => { resolve("success1"); reject("error"); resolve("success2"); }); promise .then(res => { console.log("then: ", res); }).catch(err => { console.log("catch: ", err); })
|
Promise中的状态一经改变,就不能再次进行改变了。
题目十四 ✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const promise = new Promise((resolve, reject) => { reject("error"); resolve("success2"); }); promise .then(res => { console.log("then1: ", res); }).then(res => { console.log("then2: ", res); }).catch(err => { console.log("catch: ", err); }).then(res => { console.log("then3: ", res); })
|
之所以最后是undefined,是因为catch()也会返回一个Promise,且由于这个Promise没有返回值
题目十五 ✨
1 2 3 4 5 6 7 8 9 10 11 12
| Promise.resolve(1) .then(res => { console.log(res); return 2; }) .catch(err => { return 3; }) .then(res => { console.log(res); });
|
return 2 会被包装成 Promise(2)
题目十六 ✨
1 2 3 4 5 6 7 8 9 10 11 12 13
| Promise.reject(1) .then(res => { console.log(res); return 2; }) .catch(err => { console.log(err); return 3 }) .then(res => { console.log(res); });
|
题目十七 ✨ ✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const promise = new Promise((resolve, reject) => { setTimeout(() => { console.log('timer') resolve('success') }, 1000) }) const start = Date.now(); promise.then(res => { console.log(res, Date.now() - start) }) promise.then(res => { console.log(res, Date.now() - start) })
|
这里的.then之所以都会执行,是因为Promise中的.then或者.catch可以被调用多次。
题目十八 ✨ ✨
1 2 3 4 5 6 7 8
| Promise.resolve().then(() => { return new Error('error!!!') }).then(res => { console.log("then: ", res) }).catch(err => { console.log("catch: ", err) })
|
返回任意一个非promise的值都会包裹成promise对象,这里的return new Error(‘error!!!’)被包装成 return Promise.resolve(new Error(‘error’)),如果需要抛出错误,可以使用Promise.reject()或者throw
题目十九 ✨ ✨
1 2 3 4 5
| const promise = Promise.resolve().then(() => { return promise; }) promise.catch(console.err)
|
Promise的.then或者.catch中返回的值不能是promise本身,否则会造成死循环、报错
题目二十 ✨ ✨
1 2 3 4 5
| Promise.resolve(1) .then(2) .then(Promise.resolve(3)) .then(console.log)
|
.then或者.catch中的参数期望是函数,如果传入非函数那么就会发生值穿透,第一个then和第二个then中传入的都不是函数,因此发生了值的透传,将resolve(1)传入到了最后一个then中。
题目二十一 ✨
1 2 3 4 5 6 7 8 9
| Promise.reject('err!!!') .then((res) => { console.log('success', res) }, (err) => { console.log('error', err) }).catch(err => { console.log('catch', err) })
|
then函数中会有两个参数,第一个为传入resolve结果的,第二个为reject,可以理解为.catch为.then的简写.then(undeifined,err)
1 2 3 4 5 6 7 8 9
| Promise.resolve() .then(function success (res) { throw new Error('error!!!') }, function fail1 (err) { console.log('fail1', err) }).catch(function fail2 (err) { console.log('fail2', err) })
|
题目二十二 ✨ ✨ ✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Promise.resolve('1') .then(res => { console.log(res) }) .finally(() => { console.log('finally') }) Promise.resolve('2') .finally(() => { console.log('finally2') return '我是finally2返回的值' }) .then(res => { console.log('finally2后面的then函数', res) })
|
为什么finally要在finally2中,看下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| Promise.resolve('1') .finally(() => { console.log('finally1') throw new Error('我是finally中抛出的异常') }) .then(res => { console.log('finally后面的then函数', res) }) .catch(err => { console.log('捕获错误', err) })
|
四、Promise中的all和race
题目二十三-all ✨
1 2 3 4 5 6 7 8 9 10
| function runAsync (x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000)) return p } Promise.all([runAsync(1), runAsync(2), runAsync(3)]) .then(res => console.log(res))
|
有了all,可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,.all后面的then接收的是所有异步操作的结果。
题目二十四-all ✨ ✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function runAsync (x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000)) return p } function runReject (x) { const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)) return p } Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)]) .then(res => console.log(res)) .catch(err => console.log(err))
|
题目二十五-race ✨
1 2 3 4 5 6 7 8 9 10 11
| function runAsync (x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000)) return p } Promise.race([runAsync(1), runAsync(2), runAsync(3)]) .then(res => console.log('result: ', res)) .catch(err => console.log(err))
|
虽然race是赛跑且只会获取最先执行的那个结果,但是 2和3还是会去执行的。
应用场景是:为异步请求设置超时时间。
题目二十六-race✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function runAsync(x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000) ); return p; } function runReject(x) { const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x) ); return p; } Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)]) .then(res => console.log("result: ", res)) .catch(err => console.log(err));
|
all与race小结
- Promise.all的作用是接收一组异步任务,然后并行执行异步任务,在所有异步任务执行完毕后才执行回调。
- Promise.race也是接收一组异步任务,然后并行执行异步任务,只保留第一个先完成的异步执行结果,其它的结果会抛弃不用。
- Promise.all.then中的结果与传入到all中的顺序是一致的。
- all和race传入的数组如果有抛出异常的错误,那么只会抛出第一个错误。
五、async和await
在很多时候,async和await的解法和Promise差不多,但是有些又不一样。
题目二十七 ✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); } async function async2() { console.log("async2"); } async1(); console.log('start')
|
await后面的语句相当于放到了Prmise中,下一行以及之后的语句相当于放到了.then中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| async function async1() { console.log("async1 start"); new Promise(resolve => { console.log("async2") resolve() }).then(res => console.log("async1 end")) } async function async2() { console.log("async2"); } async1(); console.log("start")
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| async function async1() { console.log("async1 start"); new Promise(resolve => { console.log('promise') }) console.log("async1 end"); } async1(); console.log("start")
|
题目二十八 ✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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")
|
题目二十九 ✨✨✨
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
| 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")
|
题目三十 ✨
1 2 3 4 5 6 7
| async function fn () { return 123 } fn().then(res => console.log(res))
|
正常情况下,await后面是一个Promise对象,会返回改对象的结果
题目三十一 ✨✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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')
|
在async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,因此相当于一直在await,await,await却始终没有响应…
所以在await之后的内容是不会执行的,也包括async1后面的 .then。
题目三十二 ✨✨
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('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')
|
题目三十三 ✨✨
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
| 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') }) })
|
题目三十四 ✨✨
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 29 30 31 32 33
| 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')
|
题目三十五 ✨✨
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 29 30 31 32
| 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...");
|
六、async处理错误
题目三十六 ✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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))
|
await后面跟着一个rejected的promise,那么就会抛出错误,不再往下执行
1 2 3 4 5 6
| async function async1 () { console.log('async1'); throw new Error('error!!!') return 'async1 success' } async1().then(res => console.log(res))
|
同上
题目三十七 ✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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')
|
1 2 3 4 5 6 7 8 9
| async function async1 () { 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')
|
七、综合题
题目三十八 ✨✨
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
| 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);
|
题目三十九 ✨✨
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 29 30
| 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)
|
这里需要注意的一点是:async函数中await的new Promise要是没有返回值的话则不执行后面的内容。
题目四十 ✨✨✨
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const p1 = new Promise((resolve) => { setTimeout(() => { resolve('resolve3'); console.log('timer1') }, 0) resolve('resolve1'); resolve('resolve2'); }).then(res => { console.log(res) setTimeout(() => { console.log(p1) }, 1000) }).finally(res => { console.log('finally', res) })
|
八、大厂面试题
题目四十一–使用Promise每秒输入1 2 3
1 2 3 4 5 6 7 8
| const arr = [1, 2, 3] arr.reduce((p, x) => { return p.then(() => { return new Promise(r => { setTimeout(() => r(console.log(x)), 1000) }) }) }, Promise.resolve())
|
题目四十二–使用Promise实现红绿灯交替重复亮
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 29 30 31
| function red { console.log('red') } function yellow(){ console.log('yellow') } function green(){ console.log('green') } const light = function (timer, cb) { return new Promise(resolve => { setTimeout(() => { cb() resolve() }, timer) }) } const step = function () { Promise.resolve().then(() => { return light(3000, red) }).then(() => { return light(2000, green) }).then(() => { return light(1000, yellow) }).then(() => { return step() }) }
step();
|
题目四十三:封装一个异步加载图片的方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| function loadImg(url){ return new Promise((resolve,reject)=>{ const img = new Image(); img.onload = function(){ console.log('一张图片加载完成') resolve(img) } img.onerror = function() { reject(new Error('Could not load image at' + url)); }; img.src = url; }) }
|