Promise最全解!
一. Promise特性类问题
1.说说你理解的Promise
(1)代理对象: Promise 对象是一个代理对象。Promise对象接受传入的 executor(执行器)作为入参,将异步任务的成功和失败分别绑定到对应的处理方法上去。
(2)Promise对象的两个特点:
**三个状态:**一个 Promise 实例有三种状态:
• pending 状态,表示进行中。这是 Promise 实例创建后的一个初始态;
• fulfilled 状态,表示异步任务成功完成。这是在执行器中调用 resolve 后,达成的状态;
• rejected 状态,表示操作失败、被拒绝。这是在执行器中调用 reject后,达成的状态;
状态切换机制: Promise实例的状态是可以改变的,但它只允许被改变一次。当我们的实例状态从 pending 切换为 rejected 后,就无法再扭转为 fulfilled,反之同理。当 Promise 的状态为fulfilled 时,会触发其对应的 then 方法入参里的 onfulfilled 函数;当 Promise 的状态为 rejected 时,会触发其对应的 catch 方法入参里的 onrejected 函数。
2. Promise的出现是为了解决什么问题
为了解决大量异步链式调用出现的回调地狱问题。
3. Promise 常见方法有哪些?各自是干嘛的?
1.then(): 分别指定resolved状态和rejected状态的回调函数
第一参数: 状态变为resolved时调用
第二参数: 状态变为rejected时调用(可选))
2.catch(): 指定发生错误时的回调函数
3.Promise.all(iterable) ,返回一个新的 promise 对象,该 promise 对象在 所有的 promise 对象都成功的时候才会触发成功, 一旦有任何一个 promise 对象失败则立即触发该 promise 对象的失败。该 promise 也会用子 promise 的成功返回值或失败详情作为参数调用 promise 绑定的相应处理函数,成功的返回值为所有的promise对象的成功返回值组成的数组。
Promise.all 里的任务列表是按照顺序一个一个发起的。
但是它们是异步的,互相之间并不阻塞,每个任务完成时机是不确定的,尽管如此,所有任务结束之后,它们的结果仍然是按顺序地映射到resultList里,这样就能和Promise.all里的任务列表一一对应起来。
var p1 = Promise.resolve('1号选手');
var p2 = '2号选手';
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "3号选手");
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // ["1号选手", "2号选手", "3号选手"]
});
- Promise.race(iterable),这个方法顾名思义,就是取出响应最快的那个请求。返回一个promise对象,该promise对象在任意一个子 promise 被成功或失败后,会立即被触发成功或者失败。该 promise 也会用子 promise 的成功返回值或失败详情作为参数调用 promise 绑定的相应处理函数
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "1号选手");
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 50, "2号选手");
});
// 这里因为 2 号选手返回得更早,所以返回值以 2号选手为准
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "2号选手"
});
5.Promise.resolve(value):它返回一个 Promise 对象,传入的参数分为以下两种:
传入的是promise对象,返回的Promise对象的最终状态由传入的promise对象的状态决定
传入的是一个值,返回的 Promise 对象状态为 fulfilled,同时这里的 value 会作为 then 方法中指定的 onfulfilled 的入参。
传入的是Thenable对象(指具有then方法的对象),Promise.resolve方法会将这个对象转为Promise对象,然后立即执行thenable对象的then方法
6.Promise.reject(reason): 返回一个状态为失败的Promise对象,并将reason传递给对应的处理方法。
4.promise的缺点
- 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
- Promise对象提供统一的的接口,使得控制异步操作更加容易
5.Promise缺点:
- 无法取消,一旦新建它就会立即执行,无法中途取消;
- 如果不设置回调函数,Promise内部抛出的错误,不会反映到外部;
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚开始还是即将完成)
二. 面试代码类问题
1.Promise 中的处理函数是异步任务,then 方法中传入的任务是一个异步任务。
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
// 1 2 4 3
生成Promise实例后,先打印1,resolve() 这个调用,作用是将 Promise 的状态从 pending 置为 fulfilled,这个新状态会让 Promise 知道“我的 then 方法中那个任务可以执行了”——注意是”可以执行了“,而不是说”立刻就会执行“。毕竟作为一个异步任务,它的基本修养就是要等同步代码执行完之后再执行。所以说数字 3 的输出排在最后。
- Promise 对象的状态只能被改变一次
const promise = new Promise((resolve, reject) => {
resolve('第 1 次 resolve')
console.log('resolve后的普通逻辑')
reject('error')
resolve('第 2 次 resolve')
})
promise
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
// resolve后的普通逻辑
// then: 第 1 次 resolve
3.Promise 值穿透问题
Promise.resolve(1)
.then(Promise.resolve(2)) // then方法不识别
.then(3) // then方法不识别
.then() // then方法不识别
.then(console.log) // then方法终于识别
// 1
then参数期望是函数,onFulfilled(成功态的处理函数)和 onRejected(失败态的处理函数)。传入非函数则会发生值穿透。值传透可以理解为,当传入then的不是函数的时候,这个then是无效的。
- 一个promise指定多个成功/失败回调函数, 都会调用吗?
答:当promise改变为对应状态时都会调用
let p = new Promise((resolve, reject) => {
resolve('Promise状态会被标记为resolved')
// reject('Promise状态会被标记为rejected')
// throw new Error('Promise状态会被标记为rejected')
});
p.then(
value => {
console.log('value1', value) // value1 Promise状态会被标记为resolved
},
reason => {
console.log('reason1', reason)
}
).then(
value => {
console.log('value2', value) // value2 undefined
},
reason => {
console.log('reason2', reason)
}
)
5.promise如何串连多个操作任务?
(1) promise的then()返回一个新的promise, 可以开成then()的链式调用
(2) 通过then的链式调用串连多个同步/异步任务
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行任务1(异步)")
resolve(1)
}, 1000);
}).then(
value => {
console.log('任务1的结果: ', value)
console.log('执行任务2(同步)')
return 2
}
).then(
value => {
console.log('任务2的结果:', value)
return new Promise((resolve, reject) => {
// 启动任务3(异步)
setTimeout(() => {
console.log('执行任务3(异步))')
resolve(3)
}, 1000);
})
}
).then(
value => {
console.log('任务3的结果: ', value)
}
)
6.中断promise链
(1)当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数
(2)办法: 在回调函数中返回一个pending状态的promise对象
return new Promise(()=>{}) // 返回一个pending的promise 中断promise链
三、Promise 底层原理考察
手写Promise