玄学Promise把我整神了!还好我这个重庆人本身就很神:)
理解好Promise的输入输出是理解Promise的关键所在。本文将一步步的细究Promise每一个实列方法与静态方法的输入输出,并在最后将出示几道大厂面试题用于学习后的验证。
注意:想要深入理解Promise的运行原理,必须要了解最基本的事件循环机制。二者是相辅相成,你中有我,我中有你的存在。 为防止完全不懂事件循环机制,纯粹好奇Promise的同学阅读本文,我对事件循环机制作出如下总结: 事件循环中分为了主线程,宏任务队列和微任务队列。微任务内部函数执行顺序优先度远大于宏任务
- V8引擎首先开始执行主线程,
- 遇到如console.log()这种同步代码就直接执行
- 遇到属于微任务的方法,将方法内部的回调函数加入到微任务队列中;
- 遇到属于宏任务的方法,将方法内部的回调函数加入到宏任务队列中。
- 然后主线程将所有同步代码执行完毕之后,执行微任务与宏任务中的回调函数。
- 系统会查看微任务队列中是否存在微任务,如果存在,则将所有微任务按照先后顺序(先进先出)放入主线程中全部进行执行,然后执行完成清空微任务。
- 微任务完成后,会查看宏任务队列中是否存在宏任务,如果存在,则将所有宏任务按照先后顺序(先进先出)放入主线程中全部进行执行,然后执行完成清空宏任务。
- 最后完成所有代码的执行,此为事件循环
什么是Promise?
要理解Promise,我们需要理解他的历史由来。换句话说就是,他为什么出现,解决了什么痛点,有什么缺陷。 一言以蔽之:解决回调地狱问题的一种异步编程的解决方案
创建一个Promise对象
new Promise((resolve, reject) => {
resolve() 或者 reject()
})
new Promise(cellback)
实列化一个promise对象,该代码传入一个回调函数。并且该回调函数代码内部执行逻辑为同步代码(什么是同步代码请看上文)。
我们来看看实例化一个promise对象时,内部回调函数做的事情
new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
})
new Promise((resolve, reject) => {
console.log(3);
reject();
console.log(4);
})
答案
1
2
3
4
Uncaught (in promise) undefined
// ↑这是reject引发的错误,其中的undefined是执行reject函数未传参导致的
简单来说就是new一个对象的时候,内部的回调函数我们可以当作同步代码来做
使用resolve()获取Promise内部的值
let promise1 = new Promise((resolve, reject) => {
resolve(1);
})
promise.then((res)=>{
console.log("我是第一个",res)
})
我是第一个 1
使用reject()获取Promise内部的值
let promise2 = new Promise((resolve, reject) => {
reject(2);
})
promise2.then((res)=>{
console.log("我是第一个",res)
},(res)=>{
console.log("我是第二个",res)
})
答案
我是第二个 2
Promise {<fulfilled>: undefined}
我们也能将上述代码轻松化简为如下代码
Promise.resolve(1).then((res) => {console.log("我是resolve的",res)});
Promise.reject(2).then((res) => {},(res)=>{console.log("我是reject的",res)});
实例方法:
then()
then
函数是整个使用Promise对象的精髓所在,正所谓使用了then()
函数才使得整个异步编程脱离了回调地狱之中,才让我们在网络请求之中获得我们需要的网络数据。
要理解then()
函数首先需要理解的其实是then()
函数内部的回调函数,而关键在于这个回调函数的输入与输出,在关于实例化一个promise对象时我们讲了,每一个then()
函数接收的参数为实列化对象时所调用的方法。
- 调用resolve()方法,将Promise对象的状态从“Pending”变成“fulfilled”
- 调用reject()方法,将Promise对象的状态从“Pending”变成“rejected”
注意:pending状态又称之为未决状态(unsettled),fulfilled与rejected又统称为已决状态(settled) 状态一经改变,绝对不可能再发生变化
Promise.resolve().then(() => 1); // 返回值为 new Promise(resolve => resolve(1))
Promise.resolve().then(() => Promise.resolve(2)); // 返回
Promise.resolve().then(() => {
throw new Error('abc')
}); // 返回 new Promise(resolve => resolve(Promise.reject(new Error('abc'))))
Promise.reject().then(() => 1, () = 2); // 返回值为 new Promise(resolve => resolve(2))
现在我们来彻底理清then的返回值:
简而言之:所有then()
函数内部回调函数的返回值都会包一层Promise.resolve()
Promise.resolve().then(() => {
return 1
});
// 返回值为
new Promise(resolve =>{
return resolve(1)
})
Promise.resolve().then(() => {
return Promise.resolve(2)
});
// 返回值为
new Promise(resolve => {
return resolve(Promise.resolve(2))
})
Promise.resolve().then(() => {
throw new Error('abc')
});
// 返回值为
new Promise(resolve => {
return resolve(Promise.reject(new Error('abc')))
})
Promise.reject().then(() => 1, () = 2);
// 返回值为
new Promise(resolve => {
return resolve(2)
})
.catch()
哪catch的返回值又是什么呢?
简而言之就是所有的返回值都会包一层Promise.resolve()
Promise.reject().catch(() => 3);
// 返回值为
new Promise(resolve => resolve(3))
Promise.resolve().catch(() => 4);
// 返回值为
new Promise(resolve => resolve(调用catch的promise对象))
.finally
哪catch的返回值又是什么呢? 其实道理一样的与上同理
静态方法
promise.resolve()
将现有对象转为promise对象。方法也会返回一个新的promise实例。
const p1 = Promise.resolve('p1-resolve')
p1.then((val) => {
console.log(val)
})
Promise.resolve().then(() => {
return Promise.resolve('123')
}).then((val) => {
console.log(val) // 123
})
举几个例子提高我们对该方法的理解
- 当参数是一个 Promise 实例 如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
const thenable = {
then: function(resolve, reject) {
resolve('abc')
}
};
- 给Promise.resolve()方法传入一个带有then方法的对象,会将这个对象转为 Promise 对象,然后立即执行thenable对象的then()方法。
const thenable = {
then: function(resolve, reject) {
resolve('abc')
}
}
const p1 = Promise.resolve(thenable).then((val) => {
console.log(val)
})
Promise.reject()
Promise.reject() 方法返回一个带有拒绝原因的 Promise 对象
Promise.reject(new Error('err')).catch(e => {
console.log(e.message) // err
})
Promise.all()
Promise.all()将多个Promise放在一个数组中,当整个数组的全部promise成功时才会返回成功,当数组中的promise有一个出现失败时就返回失败 (失败的原因是第一个失败promise的结果)。
简而言之:全部成功才是成功,一个失败全部打回
const delay = (ms, msg) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(msg)
}, ms)
})
}
const p1 = delay(1500, 'p1')
const p2 = delay(2000, 'p2')
const p3 = delay(1000, 'p3')
Promise.all([p1, p2, p3]).then(value => {
console.log(value) // 2s后打印 [ 'p1', 'p2', 'p3' ]
})
Promise.race()
该方法用于并发启动多个 Promise,并且只返回第一个完成(无论是 resolve 还是 reject)的 Promise 的结果。换句话说,Promise.race() 方法就像是多个 Promise 之间的赛跑,哪个 Promise 最先到达终点(即变为 fulfilled 或 rejected 状态),整个 race 就会立即结束,并采用该 Promise 的结果。
简而言之:谁最快成功我就要谁的结果
const delay = (ms, msg) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(msg)
}, ms)
})
}
const p1 = delay(1500, 'p1')
const p2 = delay(2000, 'p2')
const p3 = delay(1000, 'p3')
Promise.race([p1, p2, p3]).then(value => {
console.log(value) // p3
})
Promise.allSettled()
该方法返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都的状态都已决(不管是fulfilled还是rejected),返回的 Promise 对象的状态才会已决。
简而言之:管他成不成功,干就完了
const delay = (ms, msg, isReject = false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
isReject ? reject(msg) : resolve(msg)
}, ms)
})
}
const p1 = delay(1500, 'p1')
const p2 = delay(2000, 'p2')
const p3 = delay(1000, 'p3', true)
Promise.allSettled([p1, p2, p3]).then(value => {
console.log(value) // 2s后打印
/**
[
{ status: 'fulfilled', value: 'p1' },
{ status: 'fulfilled', value: 'p2' },
{ status: 'rejected', reason: 'p3' }
]
*/
})
Promis.any()
ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。
简而言之:全部为同一个状态时就返回同一个状态
const delay = (ms, msg, isReject = false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
isReject ? reject(msg) : resolve(msg)
}, ms)
})
}
const p1 = delay(1500, 'p1')
const p2 = delay(2000, 'p2')
const p3 = delay(1000, 'p3', true)
Promise.any([p1, p2, p3]).then(value => {
console.log(value) // 2s后打印: p1
})
转载自:https://juejin.cn/post/7366826090834804775