我在网上搜索大厂面经,总结了一些promise的经典面试题。希望能帮助到需要的同学。
更多题目,可以访问灵题库
关于promise的“说出代码执行结果”的面试题解题思路,可以参考我的另一篇文章:
一文讲透Promise面试题:说出代码执行结果
1. 说出代码执行结果(百度)
题目
说出下面代码执行结果
const promise = new Promise((resolve,reject)=>{
console.log(1);
resolve();
console.log(2);
reject()
})
setTimeout(()=>{console.log(5)},0)
promise.then(()=>{console.log(3)})
.then(()=>{console.log(6)})
.catch(()=>{console.log(7)})
console.log(4)
答案
答案是1,2,4,3,6,5
首先new Promise时候打印1和2,因为new Promise时候会立即执行传入的方法
然后后面代码都是异步代码,先将setTimeout的回调加入宏任务队列,再把promise.then放入到微任务队列,然后直接执行最后一句,打印4
这样宏任务代码执行完了,接下来开始执行微任务队列中的任务,由于promise resolve,因为promise resolve之后状态不会再改变,因此不会执行到reject的对调,所以打印3和6
微任务队列为空,再到宏任务队列中查找任务,找到setTimeout回调执行,打印5
调用栈、宏任务队列、微任务队列都为空,代码执行结束。
2. 说出代码执行结果(阿里)
题目
说出下面代码执行结果
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve();
}, 0);
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);
答案
3, 7, 4, 1, 2, 5
首先定义first
然后执行first,然后执行new Promise传入的方法,先打印3
又new Promise,执行其中传入的方法,打印7
执行setTimeout,将回调放入宏任务队列
执行resolve(1),将内部promise状态置为fullfilled,值为1
执行resolve(2),将外部promise状态置为fullfilled,值为2
执行内部promise.then方法,将回调加入微任务队列
执行first().then,即外部的promise,将回调加入到微任务队列
调用栈为空,开始从微任务队列拿取任务,首先拿到内部promise的回调,打印其值1
然后从微任务队列中拿取外部的promise的回调,打印其值2
此时微任务队列为空,开始从宏任务队列中拿取任务,即setTimeout回调,打印5。
调用栈,宏任务队列和微任务队列都为空,执行结束。
3. 说出代码执行结果(字节)
题目
说出下面代码执行结果
console.log(1);
new Promise(resolve => {
resolve();
console.log(2);
}).then(() => {
console.log(3);
})
setTimeout(() => {
console.log(4);
}, 0);
console.log(5);
答案
1,2,5,3,4
先打印1
执行new Promise的函数,打印2
执行promise.then,将回调加入微任务队列
将setTimeout的回调加入宏任务队列
打印5
调用栈为空,取微任务队列中的任务执行,打印3
微任务队列为空,取宏任务队列任务执行,打印5
调用栈、微任务队列、宏任务队列都为空,执行结束
4. 说出代码执行结果(字节)
题目
说出下面代码执行结果
Promise.resolve()
.then(() => {
console.log('1');
})
.then(() => {
console.log('2');
});
setTimeout(() => {
Promise.resolve()
.then(() => {
console.log('3');
})
.then(() => {
console.log('4');
});
setInterval(() => {
console.log('5');
}, 3000);
console.log('6');
}, 0);
答案
1,2,6,3,4,5,5...
先执行Promise.resolve,将两个回调加入到微任务队列中
执行setTimeout,将其回调加入宏任务队列
调用栈为空,拿出微任务队列中的两个回调执行,打印1,2
微任务队列为空,拿出宏任务队列中的setTimeout的回调执行
将setTimeout中的Promise.resolve的两个回调加入到微任务队列
将setTimeout中的setInterval的回调加入宏任务队列
打印6
取出微任务队列中的两个Promise的回调,打印3,4
取宏任务队列中的setInterval的回调执行,每隔3s符合执行条件,打印5。注意setInterval调用时候不马上会执行一次,第一次执行是3s以后。
5. 说出代码执行结果(网易)
题目
说出代码执行结果
setTimeout(function() {
console.log(1);
}, 0);
console.log(2);
async function s1() {
console.log(7)
await s2();
console.log(8);
}
asycn function s2() {
console.log(9);
}
s1();
new Promise((resolve, reject) => {
console.log(3);
resolve();
console.log(6);
}).then(() => console.log(4))
console.log(5);
答案
2,7,9,3,6,5,8,4,1
记住async只是promise的语法糖,转化为等价的形式就好分析了
先执行setTimeout,加入宏任务队列中
打印2
执行s1,同步打印7
执行s2,同步打印3
执行完s2,将console.log(8)加入到微任务队列
然后执行s1后面的Promise,打印3和6
执行then,将console.log(4)加入到微任务队列中
打印5
调用栈为空,将微任务队列中的两个任务依次拿出来执行,打印8和4
微任务队列执行完,将宏任务队列的任务拿出来执行,打印1
调用栈、微任务队列、宏任务队列都为空,执行完毕。
6. 代码的执行结果(拼多多)
题目
说出下面代码执行结果
function fn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('error');
}, 1000);
});
}
const foo = async () => {
try {
await fn();
} catch (e) {
console.log('lala', e); // some error
}
}
foo();
答案
打印 lala error。
如果await后面返回的promise reject掉,需要用try catch语句捕获这个reject。
7. 讲讲promise(字节、美团、拼多多)
题目
讲讲promise,promise的3种状态和状态转换。
Promise中回调函数是同步的还是异步的?then的链式调用是同步的还是异步的?
答案
promise目的:异步编程解决回调地狱,让程序开发者编写的异步代码具有更好的可读性。
promise规范规定了一种异步编程解决方案的API。规范规定了promise对象的状态和then方法。
promise是这种异步编程的解决方案的具体实现。
new Promise对象时候传入函数,函数立即执行,函数接收resolve、reject参数,调用resolve或reject时候会改变promise状态。状态改变后不会再变化。
promise状态
- pending
- fullfilled
- rejected
未调用resolve或者reject时候处于pending状态,调用resolve后处于fullfilled状态,调用reject后处于rejected状态。如果在pending状态时候,执行任务抛出错误,则变成reject状态。
状态变化后,会执行通过then注册的回调。执行顺序和调用then方法的顺序相同。
调用then方法时候,如果状态是pending则注册回调,等到状态改变时候执行,如果状态已经改变则执行相应的回调。
Promise回调函数是同步的,then回调是异步的,会被放到微任务队列中异步执行。
8. 手写promise(百度、拼多多、京东)
题目
手写promise
答案
参考这篇文章:
手写Promise
9. 实现Promise.all,Promise.race,Promise.any(百度、滴滴、网易)
题目
如何实现Promise.all、Promise.race和Promise.any方法。
答案
Promise.all,Promise.race,Promise.any的实现
function all(arr) {
return new Promise((resolve, reject) => {
let isComplete = false;
const resolveDataList = new Array(arr.length).fill(undefined);
const onFullfilled = (data, i) => {
if (isComplete) {
return;
}
resolveDataList[i] = data;
if (resolveDataList.every(item => item !== undefined)) {
resolve(resolveDataList);
}
};
const onRejected = reason => {
if (isComplete) {
return;
}
isComplete = true;
reject(reason);
}
arr.forEach((promise, index) => {
promise.then(
data => {onFullfilled(data, index)},
onRejected
);
});
});
}
function race(arr) {
return new Promise((resolve, reject) => {
let isComplete = false;
const onFullfilled = data => {
if (isComplete) {
return;
}
isComplete = true;
resolve(data);
};
const onRejected = reason => {
if (isComplete) {
return;
}
isComplete = true;
reject(reason);
};
arr.forEach(promise => {
promise.then(
onFullfilled, onRejected
);
});
});
}
function any(arr) {
return new Promise((resolve, reject) => {
let isComplete = false;
const rejectDataList = new Array(arr.length).fill(undefined);
const onFullfilled = data => {
if (isComplete) {
return;
}
isComplete = true;
resolve(data);
};
const onRejected = (reason, i) => {
if (isComplete) {
return;
}
rejectDataList[i] = reason;
if (rejectDataList.every(item => item !== undefined)) {
reject('AggregateError: All promises were rejected');
}
}
arr.forEach((promise, index) => {
promise.then(
onFullfilled,
reason => {onRejected(reason, index);}
);
});
});
}
10. Promise.all,Promise.race区别(拼多多)
题目
Promise.all,Promise.race区别是什么?
手写一个方法,使用Promise.all,实现所有都resolved/reject时才返回,并返回所有的结果
答案
区别是:
Promise.all() 全部promise成功才算成功,一个promise就算失败,成功的话,返回成功的数据数组,失败的话抛出最先失败的promise的reason。
Promise.race() 最先的promise完成则返回,promise结果和最先完成的promise一致。
手写方法:
function allComplete(arr) {
return Promise.all(arr.map(promise => {
return new Promise(
resolve => promise.then(resolve, resolve)
);
}));
}
关注「灵题库」,更多大厂面试真题解析。
网友评论