上一篇文章 JavaScript Promise基础(对于 Promise用法不熟悉的,能够先看这篇文章,理解了再来看这篇文章,会对你有很大帮助)中,介绍了Promise的基本使用,在这篇文章中,咱们将根据 Promises/A+ 规范试着本身来写一个Promise,主要是学习Promise的内部机制与它的编程思想。javascript
Promise究竟是啥玩意呢?是一个类、数组、对象、函数?好了不猜了,打印出来看看吧console.dir(Promise),是骡子是马拉出来遛遛呗! java
呀呀呀!原来 Promise 是一个构造函数,本身身上有 all、resolve、reject,原型上有 then、catch 等眼熟的方法。那就不用废话了 new 一个玩玩呗!git
let p=new Promise((resolve,reject)=>{
console.log(1);
resolve('成功');
reject('失败');
});
console.log(2);
p.then((data)=>{
console.log(data+1);
},(err)=>{
console.log(err);
})
p.then((data)=>{
console.log(data+2);
},(err)=>{
console.log(err);
})
复制代码
输出 1 2 成功1 成功2github
new Promise时传递一个函数(executor执行器是当即执行的),并接收两个参数:resolve,reject,分别表示成功的回调函数与失败的回调函数。每个实例都有一个 then 方法,参数是成功和失败,成功会有成功的值,失败会有失败的缘由,而且成功就不能失败反之也同样。同一个 Promise 能够屡次 then……貌似跑偏了呀!大家都懂也应该会用,仍是回到主题开始本身实现吧!npm
class Promise{
constructor(executor){
this.status='pending'; //默认的状态
this.value=undefined; //默认成功的值
this.reason=undefined; //默认失败的缘由
this.onResolvedCallbacks=[];//存放成功的数组
this.onRejectedCallbacks=[];//存放失败的数组
let resolve=(value)=>{
if(this.status==='pending'){//这里的判 断是为了防止executor中调用两次resovle或reject方法
this.status='resolved';//成功了
this.value=value;//成功的值
}
}
let reject=(reason)=>{
if(this.status==='pending'){
this.status='rejected';//失败了
this.reason=reason;//失败的缘由
}
}
try {//捕获异常
executor(resolve,reject);//默认让执行器执行
} catch (err) {
reject(err)
}
}
then(onFufilled,onRejected){
if(this.status==='resolved'){
onFufilled(this.value);
}
if(this.status==='rejected'){
onRejected(this.reason);
}
}
}
let p=new Promise((resolve,reject)=>{
resolve('成功');
//reject('失败');
})
p.then((data)=>{
console.log(data);
},(err)=>{
console.log(err);
})
复制代码
执行输出成功,那么问题来了,咱们费劲半天写 Promise 上来就执行有啥用?使用 Promise 时咱们通常会写一些异步代码,等到异步操做执行完才会触发 resolve 或者 reject 函数。但是如今当执行 then 方法的时候此时的状态仍是初始的pending 状态,因此为了能取到参数,咱们能够经过发布订阅模式来实现。编程
then 方法里添加以下代码,当状态为 pending 时,咱们先把回调函数存到对应的数组里,等待调用。数组
if(this.status==='pending'){
this.onResolvedCallbacks.push(()=>{
onFufilled(this.value);
});
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason);
})
}
复制代码
resolve 和 reject 方法里分别添加以下代码,当调用的时候,把对应数组里的函数依次执行promise
this.onResolvedCallbacks.forEach(fn=>fn());
this.onRejectedCallbacks.forEach(fn=>fn());
复制代码
咱们都知道 Promise 有一个最为重要的 then 方法。为了保证链式调用, then 方法调用后返回一个新的 Promise,会将这个 Promise 的值传递给下一次 then 中。而且上一次 then 中不论是成功仍是失败或是返回一个普通值,都会传递到下一次 then 的参数。异步
首先咱们知道,then 是有返回值的。并且能够一直 then 下去,因此以前的 then 必须返回一个新的 Promise。因此咱们根据Promises/A+对 then 方法改造以下。函数
then(onFufilled,onRejected){
let promise2;
if(this.status==='resolved'){
promise2=new Promise((resolve,reject)=>{
let x=onFufilled(this.value);
//判断p是否是一个promise,若是是取它的结果做为promise2成功的结果,
//若是返回一个普通值,一样做为promise2成功的结果
resolvePromise(promise2,x,resolve,reject);//解析p和promise2之间的关系
});
}
if(this.status==='rejected'){
promise2=new Promise((resolve,reject)=>{
let x=onRejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
})
}
if(this.status==='pending'){//当前既没有成功,也没有失败
promise2=new Promise((resolve,reject)=>{
this.onResolvedCallbacks.push(()=>{//存放成功的回调
let x=onFufilled(this.value);
resolvePromise(promise2,x,resolve,reject);
}
);
this.onRejectedCallbacks.push(()=>{//存放失败的回调
let x=onRejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
})
})
}
return promise2;//调用then后返回一个新的promise
}
复制代码
resolvePromise 是干啥的呢?因为 then 可能返回任意值,因此根据Promises/A+规范对 then 返回的值进行以下处理或解析。
function resolvePromise(promise2,x,resolve,reject){
//判断x是否是promise
//若是当前返回的promise和x引用同一个对象报类型错误(不能本身等待本身完成)
if(promise2===x){
return reject(new TypeError('循环引用'));
}
//x不是null而且是对象或函数时,多是promise
if(x!==null&&(typeof x==='object'|| typeof x==='function')){
let called; //标识当前promise有没有调用过
try{//尽可能让别人瞎写,防止取then时出现异常
let then=x.then;//取x的then看是否是函数
if(typeof then==='function'){//若是是函数就认为它是promise
then.call(x,(y)=>{//第一个参数是this,后面的是成功的回调和失败的回调
if(called) return;
called=true;
resolvePromise(promise2,y,resolve,reject);//若是y是promise继续递归解析
},(err)=>{//只要有一个失败了就失败了
if(called) return;
called=true;
reject(err);
})
}else{//then是一个普通对象直接成功
resolve(x);
}
}catch(e){
if(called) return;
called=true;
reject(e);
}
}else{//若是x是普通值直接成功
resolve(x);
}
}
复制代码
咱们用 Promise 时发现,当不给 then 中传入参数时,后面的 then 依旧能够获得以前 then 的返回值。例如:p.then().then(),这就是值的穿透。
then(onFufilled,onRejected){
//解决onFufilled或onRejected没有传的问题
onFufilled=typeof onFufilled==='function'?onFufilled:d=>d;
onRejected=typeof onRejected==='function'?onRejected:e=>{throw e};
let promise2;
if(this.status==='resolved'){
promise2=new Promise((resolve,reject)=>{
let x=onFufilled(this.value);
//判断p是否是一个promise,若是是取它的结果做为promise2成功的结果,
//若是返回一个普通值,一样做为promise2成功的结果
resolvePromise(promise2,x,resolve,reject);//解析p和promise2之间的关系
});
}
if(this.status==='rejected'){
promise2=new Promise((resolve,reject)=>{
let x=onRejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
})
}
if(this.status==='pending'){//当前既没有成功,也没有失败
promise2=new Promise((resolve,reject)=>{
this.onResolvedCallbacks.push(()=>{//存放成功的回调
let x=onFufilled(this.value);
resolvePromise(promise2,x,resolve,reject);
}
);
this.onRejectedCallbacks.push(()=>{//存放失败的回调
try{
let x=onRejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
})
}
return promise2;//调用then后返回一个新的promise
}
复制代码
executor 执行的时候咱们在外面包了 try{}catech 可是咱们内部代码是异步的,就没法捕获错误了,须要给每一个 then 中的方法都加一个 try{}catch
then(onFufilled,onRejected){
//解决onFufilled或onRejected没有传的问题
onFufilled=typeof onFufilled==='function'?onFufilled:d=>d;
onRejected=typeof onRejected==='function'?onRejected:e=>{throw e};
let promise2;
if(this.status==='resolved'){
promise2=new Promise((resolve,reject)=>{
setTimeout(() => {
try{
let x=onFufilled(this.value);
//判断p是否是一个promise,若是是取它的结果做为promise2成功的结果,
//若是返回一个普通值,一样做为promise2成功的结果
resolvePromise(promise2,x,resolve,reject);//解析p和promise2之间的关系
}catch(e){
reject(e);
}
}, 0);
});
// return promise2;
}
if(this.status==='rejected'){
promise2=new Promise((resolve,reject)=>{
setTimeout(() => {
try{
let x=onRejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
}, 0);
})
// return promise2;
}
if(this.status==='pending'){//当前既没有成功,也没有失败
promise2=new Promise((resolve,reject)=>{
this.onResolvedCallbacks.push(()=>{//存放成功的回调
setTimeout(() => {
try{
let x=onFufilled(this.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(()=>{//存放失败的回调
setTimeout(() => {
try{
let x=onRejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
}, 0);
})
})
// return promise2;
}
return promise2;//调用then后返回一个新的promise
}
复制代码
catch 接收的参数只有错误,也就至关于 then 方法没有成功的简写。并且 catch 后依然能够 then,那就简单暴力上代码吧!
catch(onRejected){
return this.then(null,onRejected);
}
复制代码
Promise.resolve()、Promise.reject() 这两种用法,是直接能够经过类调用的,原理就是返回一个内部是resolve 或 reject 的 Promise 对象。
Promise.resolve=function(val){
return new Promise((resolve,reject)=>{
resolve(val)
})
}
Promise.reject=function(val){
return new Promise((resolve,reject)=>{
reject(val)
})
}
复制代码
all方法的做用就是将一个数组的 Promise 对象放在其中,当所有 resolve 的时候就会执行 then 方法,当有一个 reject 的时候就会执行 catch,而且他们的结果也是按着数组中的顺序来的.
Promise.all = function(promises){
let arr = [];
let i = 0;
function processData(index,data){
arr[index] = data;
i++;
if(i == promises.length){
resolve(arr);
}
}
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(data=>{
processData(i,data);
},reject)
}
})
}
复制代码
写了这么多,到底符不符合Promises/A+规范呢?
//promise的语法糖
Promise.defer=Promise.deferred=function(){
let dfd={};
dfd.promise=new Promise((resolve,reject)=>{
dfd.resolve=resolve;
dfd.reject=reject;
})
return dfd;
}
复制代码
安装promises-aplus-tests用来测试,安装:npm install promises-aplus-tests -g,测试:promises-aplus-tests + "文件名"。
到这基本就简单实现了一个本身的 Promise,此时对 Promise 的内部机制与它的编程思想有没有更深刻的理解呢?新手可能一脸懵逼,大牛可能一脸蔑视。但愿你们都有收获。写的很差,有问题欢迎你们在评论区评论指正(还不快去点赞⊙﹏⊙)!!!