「超全」手写 Promise 的相关方法
原文发布在: 手写 Promise 的相关方法.
摘要
Promise 作为 JS 社区的异步解决方案, 为开发者提供了. then(),Promise.resolve(),Promise.reject() 等基本方法. 除此之外, 为了更方便地组合和控制多个的 Promise 实例, 也提供了. all(),.race() 等方法.
本文会在 Promise 的基本方法上, 手动实现更高级的方法, 来加深对 Promise 的理解:
实现 Promise.all
实现 Promise.race
实现 Promise.any
实现 Promise.allSettled
实现 Promise.finally
完整代码和用例请到 GitHub.com/dongyuanxin/diy-promise.
实现 Promise.all
过程
Promise.all(iterators) 返回一个新的 Promise 实例. iterators 中包含外界传入的多个 promise 实例.
对于返回的新的 Promise 实例, 有以下两种情况:
如果传入的所有 promise 实例的状态均变为 fulfilled, 那么返回的 promise 实例的状态就是 fulfilled, 并且其 value 是 传入的所有 promise 的 value 组成的数组.
如果有一个 promise 实例状态变为了 rejected, 那么返回的 promise 实例的状态立即变为 rejected.
代码实现
实现思路:
传入的参数不一定是数组对象, 可以是 "遍历器"
传入的每个实例不一定是 promise, 需要用 Promise.resolve() 包装
借助 "计数器", 标记是否所有的实例状态均变为 fulfilledPromise.myAll=function(iterators){
constpromises=Array.from(iterators);
constnum=promises.length;
constresolvedList=newArray(num);
letresolvedNum=0;
returnnewPromise((resolve,reject)=>{
promises.forEach((promise,index)=>{
Promise.resolve(promise)
.then(value=>{
// 保存这个 promise 实例的 value
resolvedList[index]=value;
// 通过计数器, 标记是否所有实例均 fulfilled
if(++resolvedNum===num){
resolve(resolvedList);
}
})
.catch(reject);
});
});
};
实现 Promise.race
过程
Promise.race(iterators) 的传参和返回值与 Promise.all 相同. 但其返回的 promise 的实例的状态和 value, 完全取决于: 传入的所有 promise 实例中, 最先改变状态那个 (不论是 fulfilled 还是 rejected).
代码实现
实现思路:
某传入实例
pending -> fulfilled
时, 其 value 就是 Promise.race 返回的 promise 实例的 value
某传入实例 pending -> rejected 时, 其 error 就是 Promise.race 返回的 promise 实例的 errorPromise.myRace=function(iterators){
constpromises=Array.from(iterators);
returnnewPromise((resolve,reject)=>{
promises.forEach((promise,index)=>{
Promise.resolve(promise)
.then(resolve)
.catch(reject);
});
});
};
实现 Promise.anyPromise.any=function(iterators){
constpromises=Array.from(iterators);
constnum=promises.length;
constrejectedList=newArray(num);
letrejectedNum=0;
returnnewPromise((resolve,reject)=>{
promises.forEach((promise,index)=>{
Promise.resolve(promise)
.then(value=>resolve(value))
.catch(error=>{
rejectedList[index]=error;
if(++rejectedNum===num){
reject(rejectedList);
}
});
});
});
};constformatSettledResult=(success,value)=>
success
?{status:"fulfilled",value}
:{status:"rejected",reason:value};
Promise.allSettled=function(iterators){
constpromises=Array.from(iterators);
constnum=promises.length;
constsettledList=newArray(num);
letsettledNum=0;
returnnewPromise(resolve=>{
promises.forEach((promise,index)=>{
Promise.resolve(promise)
.then(value=>{
settledList[index]=formatSettledResult(true,value);
if(++settledNum===num){
resolve(settledList);
}
})
.catch(error=>{
settledList[index]=formatSettledResult(false,error);
if(++settledNum===num){
resolve(settledList);
}
});
});
});
};newPromise(resolve=>{
setTimeout(()=>resolve(1),1000);
})
.then(value=>console.log(Date.now()))
.catch(error=>console.log(Date.now()));newPromise(resolve=>{
setTimeout(()=>resolve(1),1000);
}).finally(()=>console.log(Date.now()));Promise.prototype.finally=function(cb){
returnthis.then(
value=>Promise.resolve(cb()).then(()=>value),
error=>
Promise.resolve(cb()).then(()=>{
throwerror;
})
);
};GitHub.com/tc39/proposal-promise-allSettled
GitHub.com/matthew-andrews/Promise.prototype.finally
来源: http://www.jianshu.com/p/b4e8295c9bf3