什么是Promise
Promise是用来解决异步问题的一种方案。
原理:用构造函数将异步操作进行封装,在异步执行结束后,根据任务的成功或失败改变Promise的状态,在Promise实例对象的then方法中可获取异步操作的值。
为什么要用Promise
1.Promise之前的异步是如何实现的?
是通过纯回调函数实现的:
getFile(syncFunc,successCallback,errorCallback);
多个串行且相互依赖的异步操作如下:
// 回调地狱
getFile(data,successCallback(result1){
getFileTwo(result1,successCallbackTwo(result2){
getFileThree(result2,successCallbackThree(result3){
// 处理数据
},errorCallback3)
},errorCallback2)
},errorCallback1)
可以看出,回调方式的异步串行会造成回调地狱,很难维护,并且错误处理与正常业务代码耦合在一起,牵一发而动全身。
如何使用promise
在使用promise的同时,我们顺便用代码手写一个Promise,加深理解。
基本使用
// 将异步封装在promise构造函数的执行器当中
const promise1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('success')
// 测试状态是否可被改变
reject('error')
},1000)
})
// 用then方法获取异步数据
const promise2 = promise1.then(value=>{
console.log('第一次获取异步数据',value);
throw new Error('error')
},reason=>{
console.log('第一次获取异步数据',reason);
})
promise2.then(res=>{
console.log('第二次获取异步数据',res);
},reason=>{
console.log('第二次获取异步数据',reason);
})
//结果 1s后控制台打印
'第一次获取异步数据','success'
'第二次获取异步数据','Failed:error'
以上代码证明了promise的基本功能点:
1.promise状态不可变性。resolve和reject可以将promise的状态从pending变为成功或者失败,一旦状态改变不可逆。
2.promise支持异步操作。异步结束会根据异步操作的结果(promise状态)执行then中相应的回调函数。
//基础promise 自实现
class Promise {
constructor(excutor){
// 状态
this.status = 'pending';
// 存放成功的值
this.value = '';
// 存放失败的理由
this.reason = '';
// 存放成功后的回调
this.resolvedCbs = [];
// 存放失败后的回调
this.rejectedCbs = [];
let resolve = (value) => {
// 防止调用两次resolve reject
if(this.status === 'pending'){
this.status = 'resolved';
this.value = value;
// setTimeout使回调异步执行
setTimeout(() => {
this.resolvedCbs.forEach(fn=>fn())
}, 0);
}
}
let reject = (reason) => {
// 防止调用两次resolve reject
if(this.status === 'pending'){
this.status = 'rejected';
this.reason = reason;
// setTimeout使回调异步执行
setTimeout(() => {
this.rejectedCbs.forEach(fn=>fn())
}, 0);
}
}
try{
// 创建实例的时候 同步执行执行器函数
excutor(resolve,reject);
} catch (err){
// 执行器抛出错误 表示promise失败
reject(err)
}
}
then(onResolved,onRejected){
if(this.status === 'resolved'){
onResolved(this.value)
}
if(this.status === 'rejected'){
onRejected(this.reason)
}
// 这里是实现异步的关键 异步操作还没完成时调用then,会将回调函数先存起来,状态改变之后再依次调用
if(this.status === 'pending'){
this.resolvedCbs.push(onResolved)
this.rejectedCbs.push(onRejected)
}
}
以上,我们已经实现了一个简单的promise,包含异步操作和then获取。
promise最出色的部分在于它用链式结构解决了回调地狱,所以必须要实现then的链式调用,这就要求then方法中要返回一个promise。
// promise 链式调用案例
const promise1 = new Promise((resolve,reject)=>{
resolve('成功1')
})
const promise2 = promise1.then(res=>{
console.log('第一次结果',res)
return new Promise((resolve,reject)=>resolve('成功2'))
})
const promise3 = promise2.then(res=>{
console.log('第二次结果',res)
return 3
})
promise3.then(res=>{
console.log('第三次结果',res)
})
结果:
第一次结果 成功1
第二次结果 成功2
第三次结果 3
以上使用案例需要我们手写promise时加的功能点有:
1.then函数返回一个新的promise
2.这个新的promise的结果决定了下一个then中,该执行成功回调还是失败回调
3.新的promise的结果由then回调函数的结果决定。
4.其实只是在then源码外层包了一层promise,跟then本身返回的promise/函数/undefined没有任何关系
5.then中的回调函数执行结果,直接影响then外层包裹的promise的状态。
class Promise {
constructor(executor){
this.value = undefined;
this.reason = undefined;
this.status = 'PENDING';
this.onResolveCallbacks = [];
this.onRejectCallbacks = [];
try{
executor(this.resolve,this.reject);
} catch(err) {
this.reject(err)
}
}
resolve = (value) => {
// 防止调用两次resolve/reject
if(this.status === 'PENDING'){
this.value = value;
this.status = 'RESOLVED';
// 发布通知
this.onResolveCallbacks.forEach(fn=>fn(this.value))
}
}
// 防止调用两次resolve/reject
reject = (reason) => {
if(this.status === 'PENDING'){
this.reason = reason;
this.status = 'REJECTED';
// 发布通知
this.onRejectCallbacks.forEach(fn=>fn(this.reason))
}
}
catch(onrejected){
this.then(undefined,onrejected)
}
then(onfulfilled,onrejected){
// 以下两句是为了实现结果透传 then().then()
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : value => value;
onrejected = typeof onrejected === 'function' ? onrejected : error => {throw error}
const promise2 = new Promise((resolve,reject)=>{
function handlePromise(x){
let called;
/*
为什么不能用x.then instanceof Promise实现?
使得我们定义的promise更具兼容性
*/
if((typeof x === 'object' && x !== null) || typeof x === 'function'){
if(typeof x.then === 'function'){
x.then(value=>{
// 防止调用两次resolve/reject
if(called) return;
called = true;
handlePromise(value)
},reason=>{
if(called) return;
called = true;
reject(reason)
})
} else {
resolve(x)
}
} else {
if(called) return;
called = true;
resolve(x)
}
}
if(this.status === 'RESOLVED'){
/* 为什么要加setTimeout?
* then中的方法是异步执行的,这里用setTimeout代替实现异步效果
*/
setTimeout(() => {
try {
// 执行onfulfilled的时候可能会直接抛出错误 reject
const x = onfulfilled(this.value);
handlePromise(x);
} catch (error) {
reject(error)
}
}, 0);
}
if(this.status === 'REJECTED'){
setTimeout(() => {
try {
const x = onrejected(this.reason);
handlePromise(x);
} catch (error) {
reject(error)
}
}, 0);
}
if(this.status === 'PENDING'){
// 实现订阅的功能
this.onResolveCallbacks.push(()=>{
setTimeout(() => {
try {
const x = onfulfilled(this.value);
handlePromise(x);
} catch (error) {
reject(error)
}
}, 0);
});
this.onRejectCallbacks.push(()=>{
setTimeout(() => {
try {
const x = onrejected(this.reason);
handlePromise(x);
} catch (error) {
reject(error)
}
}, 0);
});
}
})
return promise2;
}
}
Promise.resolve = (x) => {
/*
规则:将对象转换成promise对象
1.处理promise实例 原封不动返回实例
2.处理thenable对象或者函数 执行then函数
3.处理其他
*/
return new Promise((resolve,reject)=>{
if((typeof x === 'object' && x != null) || typeof x === 'function'){
let then = x.then;
if(typeof then === 'function'){
then.call(x,v=>resolve(v),reject)
} else {
resolve(x)
}
} else {
resolve(x)
}
})
}
Promise.reject = (x) => {
/*
规则:返回一个状态为rejected状态的promise对象
不管是啥都reject
*/
return new Promise((resolve,reject)=>{
reject(x)
})
}
Promise.all = (promises) => {
/*
规则:返回一个状态为rejected状态的promise对象
不管是啥都reject
*/
return new Promise((resolve,reject)=>{
let promiseArr = [];
promises.forEach(x=>{
if((typeof x === 'object' && x != null) || typeof x === 'function'){
let then = x.then;
if(typeof then === 'function'){
then.call(x,value=>{
promiseArr.push(value)
if(promiseArr.length === promises.length) {
resolve(promiseArr)
}
},reason=>{
reject(reason);
})
} else {
promiseArr.push(x)
}
} else {
promiseArr.push(x)
}
if(promiseArr.length === promises.length) {
resolve(promiseArr)
}
})
})
}
Promise.race = (promises) => {
return new Promise((resolve,reject)=>{
for (let x = 0; x < promises.length; x++) {
if((typeof x === 'object' && x != null) || typeof x === 'function'){
let then = x.then;
if(typeof then === 'function'){
then.call(x,resolve,reject)
} else {
resolve(x)
}
} else {
resolve(x)
}
}
})
}
以上,手写实现了一个完整的promise,可以通过promise/A+的所有测试用例。
` 1.Promise相当于一个容器,存放异步或同步操作
2.返回的promise是一个对象,里面存放了promise成功/失败的信息
`