前边《Promise详解》中我们对Promise做了介绍,这篇文章我们再来深入探讨下Promise的原理,并自己模拟实现下。
首先,Promise肯定是一个类,有三个状态,pending、resolved、rejected。状态改变要么是从pending变为resolved为成功,要么从pending变为rejected为失败。
那我们如何实现Promise呢,我们可以参考《Promises/A+》,这本书只是一个规范,当多方封装的promise都遵循规范时才尽可能通用,接下来我们就根据文档去实现Promise。
Promise
一步步实现,先来实现 2.1 章节中的 Promise States
// 为了防止拼错,我们的状态在这都统一定义为常量
const PENDING = 'pending'; // 初始态
const FULFILLED = 'fullfilled'; // 成功态
const REJECTED = 'rejected'; // 失败态
function Promise(executor) { // executor为执行函数
let self = this; // 先缓存当前的Promise实例
self.status = PENDING; // 设置状态
self.onResolvedCallbacks = []; // 定义存放成功的回调的数组
self.onRejectedCallbacks = []; // 定义存放失败回调的数组
// 当调用此方法的时候,如果promise状态为pending的话,可以转为resolve成功态,如果失败,则return
function resolve(value) {
// 如果是初始态,则转为成功态
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value; // 成功后会得到一个值,这个值不能改
self.onResolvedCallbacks.forEach((cb) => cb(self.value)); // 调用成功的回调
}
}
// 文档中说失败态需要有reason
function reject(reason) {
// 如果是初始态,则转为失败态
if (self.status === PENDING) {
self.status = REJECTED;
self.value = reason; // 状态改为失败后,需要将失败原因reason给到value
self.onRejectedCallbacks.forEach((cb) => cb(self.value)); // 调用失败的回调
}
}
// 因为exector为用户手动传入,函数执行可能会异常,所以需要捕获,如果出错了,将promise进入reject失败态
try {
exector(resolve, reject)
} catch(e) {
// 如果这个函数失败了,失败的原因reject这个promise
reject(e)
}
}
Promise.then
然后我们接着来实现 2.2章的The then
Method
// onFullfilled 和 onRejected 分别为Promise成功或者失败后的回调
Promise.prototype.then = function(onFullfilled, onRejected) {
// 如果成功或者失败的回调没有传,则表示then里面不处理任何逻辑,就直接把值往后传递即可
onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : (value) => { return value };
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason };
// 如果当前promise的状态已经是成功状态了,则onFullfilled直接取值
if (self.status === FULFILLED) {
let res = onFullfilled(self.value);
}
if (self.status === REJECTED) {
let res = onRejected(self.value);
}
// 如果当前的成功失败态还未拿到,所以要处理pending状态,pending以后再拿到resolve或reject
if (self.status === PENDING) {
self.onResolvedCallbacks.push(onFullfilled);
self.onRejectedCallbacks.push(onRejected);
}
}
写到了这里我们还不知道运行到底怎么样呢,我们来写个case来测试一下。分写在then的几个状态里边加上console来做测试,如:
if (self.status === FULFILLED) {
console.log('fullfilled')
}
if (self.status === REJECTED) {
console.log('rejected')
}
if (self.status === PENDING) {
console.log('pending')
}
let myPromise = require('./promise');
let p1 = new myPromise(function(resolve, reject) {
setTimeout(function() {
let num = Math.random();
if (num > 0.5) {
resolve(num)
} else {
reject('error')
}
})
});
p1.then(function(data) {
console.log(data)
}, function(error) {
console.error(error)
})
我们上边这个case是异步的,所以等我们执行then的时候,promise.then的时候还没有成功失败状态呢,所以肯定是pending状态,控制台打印出来'pending'。
// run code 查看结果:
// 第一次执行打印结果:
pending
0.04635508746350747
// 第二次执行打印结果:
pending
error
// 第三次执行打印结果:
pending
0.3629381823423041
...
上述是我自己测试的结果,代码都正常运行,打印逻辑看来也正确。但我们可以看到状态都走的是pending状态,为什么呢?是因为我们前边的case用的是异步,如果我们想直接测试fullfilled状态和rejected状态,则将case里的setTimeout去掉改为同步即可直接进入状态。
测试结果都正常,所以我们继续根据文档完善then。
// 代码习惯,这里我们将Promise的状态都统一定义为常量
const PENDING = 'pending'; // 初始态
const FULFILLED = 'fullfilled'; // 成功态
const REJECTED = 'rejected'; // 失败态
function Promise(executor) { // executor 为执行函数
let self = this; // 先缓存当前的Promise实例
self.status = PENDING; // 设置初始状态
self.onResolvedCallbacks = []; // 定义存放成功的回调的数组
self.onRejectedCallbacks = []; // 定义存放失败回调的数组
// 当调用此方法的时候,如果promise状态为pending的话,可以转为resolve成功态,如果失败,则return
function resolve (value){
if (value !== null && value.then && typeof value.then === 'function' {
return value.then(resolve, reject)
}
// 如果是初始态,则转为成功态
// 异步处理,直接用setTimeout就可
setTimeout(function() {
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value; // 成功后会得到一个值,这个值不能改
self.onResolvedCallbacks.forEach((cb) => cb(self.value)); // 调用成功的回调
}
})
}
// 文档中说失败态需要有reason
function reject(reason) {
// 如果是初始态,则转为失败态
setTimeout(function() {
if (self.status === PENDING) {
self.status = REJECTED;
self.value = reason; // 状态改为失败后,需要将失败原因reason给到value
self.onRejectedCallbacks.forEach((cb) => cb(self.value)); // 调用失败的回调
}
})
}
// 因为executor为手动传入,函数执行可能会异常,所以需要捕获,如果出错了,将promise进入reject失败态
try {
// 成功则函数执行
executor(resolve,reject);
} catch(e) {
// 如果这个函数失败了,失败的原因reject这个promise
reject(e)
}
}
// onFulfilled 用来接收Promise成功或者失败的原因
Promise.prototype.then = function(onFullfilled, onRejected) {
// 如果成功或者失败的回调没有传,则表示then里面不处理任何逻辑,就直接把值往后传递即可
onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : (value) => { return value };
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason };
// 如果当前promise的状态已经是成功状态了,则onFullfilled直接取值
let self = this;
let promise2;
if (self.status === FULFILLED) {
console.log('then---fullfilled')
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
try {
// res可能是一个promise对象,也可能是一个普通值
let x = onFullfilled(self.value);
// 如果获取到了返回值x,会走解析promise的过程
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
// 如果执行成功的回调过程中出现错误,则reject错误原因
reject(e)
}
})
})
}
if (self.status === REJECTED) {
console.log('then---rejected')
// let x = onRejected(self.value);
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
try {
let x = onRejected(self.reason)
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e)
}
})
})
}
// 如果当前的成功失败态还未拿到,所以要处理pending状态,pending以后再拿到resolve或reject
if (self.status === PENDING) {
console.log('then---pending')
self.onResolvedCallbacks.push(function() {
// 同上这里也需要try catch
try {
let x = onFullfilled(self.value);
// 如果取到返回值x,则会走promise解析的过程
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
});
self.onRejectedCallbacks.push(function() {
try {
let x = onRejected(self.value);
resolvePromise(promise2, x. resolve, reject);
} catch(e) {
}
});
}
}
module.exports = Promise;
上段代码中处理then的过程中有两处用到resolvePromise,接下来我们就根据文档中的2.3 章节The Promise Resolution Procedure来继续实现。
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
let called = false; // promise2 状态是否已经为 resolve 或 reject
if (x instanceof Promise) {
if (x.status === PENDING) {
x.then(function(y) {
resolvePromise(promise2, x, resolve, reject);
}, reject)
} else {
x.then(resolve, reject);
}
} else if (x !== null && ((typeof x === 'object') || (typeof x === 'function'))) {
// x是一个thenable对象或者函数,只要有then方法的对象
// 有时候我们的promise需要可能需要别的promise库进行交互使用,所以编写这段代码的时候我们需要尽可能的考虑到兼容性
try {
let then = x.then;
if (typeof then === 'function') {
// 有些promise会同时执行成功或者失败的回调
then.call(x, function(y) {
// 如果promise2已经转为成功或失败状态,则不再处理
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, function(err) {
if (called) return;
called = true;
reject(err);
});
} else {
// 这里的话x不是一个thenable对象,那直接将值进行resolve
resolve(x);
}
} catch(e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 如果x是一个普通值,则resolve x
resolve(x)
}
}
上述中的循环引用,有可能在使用的过程中错误的使用导致自己需要等待自己完成,则一直不可能完成了,所以需要做判断处理,举栗:
let p2 = p1.then(function(data) {
console.log(data);
return p2;
})
上述例子中p1.then()的返回值为p2, 而这时候如果promise不手动调用resolve方法,就无法修改状态,那就一直处在pending了。
前边在实现的过程中我们用到setTimeout来起到异步执行的作用,《Promises/A+》中第3章 Notes中也有说明,直接看文档即可。
现在我们来看一下整体代码:
// 代码习惯,这里我们将Promise的状态都统一定义为常量
const PENDING = 'pending'; // 初始态
const FULFILLED = 'fullfilled'; // 成功态
const REJECTED = 'rejected'; // 失败态
function Promise(executor) { // executor 为执行函数
let self = this; // 先缓存当前的Promise实例
self.status = PENDING; // 设置初始状态
self.onResolvedCallbacks = []; // 定义存放成功的回调的数组
self.onRejectedCallbacks = []; // 定义存放失败回调的数组
// 当调用此方法的时候,如果promise状态为pending的话,可以转为resolve成功态,如果失败,则return
function resolve (value){
if (value !== null && value.then && typeof value.then === 'function' {
return value.then(resolve, reject)
}
// 如果是初始态,则转为成功态
// 异步处理,直接用setTimeout就可
setTimeout(function() {
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value; // 成功后会得到一个值,这个值不能改
self.onResolvedCallbacks.forEach((cb) => cb(self.value)); // 调用成功的回调
}
})
}
// 文档中说失败态需要有reason
function reject(reason) {
// 如果是初始态,则转为失败态
setTimeout(function() {
if (self.status === PENDING) {
self.status = REJECTED;
self.value = reason; // 状态改为失败后,需要将失败原因reason给到value
self.onRejectedCallbacks.forEach((cb) => cb(self.value)); // 调用失败的回调
}
})
}
// 因为executor为手动传入,函数执行可能会异常,所以需要捕获,如果出错了,将promise进入reject失败态
try {
// 成功则函数执行
executor(resolve,reject);
} catch(e) {
// 如果这个函数失败了,失败的原因reject这个promise
reject(e)
}
}
// onFulfilled 用来接收Promise成功或者失败的原因
Promise.prototype.then = function(onFullfilled, onRejected) {
// 如果成功或者失败的回调没有传,则表示then里面不处理任何逻辑,就直接把值往后传递即可
onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : (value) => { return value };
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason };
// 如果当前promise的状态已经是成功状态了,则onFullfilled直接取值
let self = this;
let promise2;
if (self.status === FULFILLED) {
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
try {
// res可能是一个promise对象,也可能是一个普通值
let x = onFullfilled(self.value);
// 如果获取到了返回值x,会走解析promise的过程
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
// 如果执行成功的回调过程中出现错误,则reject错误原因
reject(e)
}
})
})
}
if (self.status === REJECTED) {
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
try {
let x = onRejected(self.reason)
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e)
}
})
})
}
// 如果当前的成功失败态还未拿到,所以要处理pending状态,pending以后再拿到resolve或reject
if (self.status === PENDING) {
self.onResolvedCallbacks.push(function() {
// 同上这里也需要try catch
try {
let x = onFullfilled(self.value);
// 如果取到返回值x,则会走promise解析的过程
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
});
self.onRejectedCallbacks.push(function() {
try {
let x = onRejected(self.value);
resolvePromise(promise2, x. resolve, reject);
} catch(e) {
}
});
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
let called = false; // promise2 状态是否已经为 resolve 或 reject
if (x instanceof Promise) {
if (x.status === PENDING) {
x.then(function(y) {
resolvePromise(promise2, x, resolve, reject);
}, reject)
} else {
x.then(resolve, reject);
}
} else if (x !== null && ((typeof x === 'object') || (typeof x === 'function'))) {
// x是一个thenable对象或者函数,只要有then方法的对象
// 有时候我们的promise需要可能需要别的promise库进行交互使用,所以编写这段代码的时候我们需要尽可能的考虑到兼容性
try {
let then = x.then;
if (typeof then === 'function') {
// 有些promise会同时执行成功或者失败的回调
then.call(x, function(y) {
// 如果promise2已经转为成功或失败状态,则不再处理
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, function(err) {
if (called) return;
called = true;
reject(err);
});
} else {
// 这里的话x不是一个thenable对象,那直接将值进行resolve
resolve(x);
}
} catch(e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 如果x是一个普通值,则resolve x
resolve(x)
}
}
module.exports = Promise;
Promise.all
Promise.all 传入一个数组, 需要等待所有的promise状态fullfilled之后才resolve,有一个失败则reject。
Promise.prototype.all = function(promiseArr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promiseArr)) {
throw new Error('argument must be an array');
}
let resultArr = [];
let count = 0;
for(let i = 0; i < promiseArr.length; i++) {
promiseArr[i].then(function(data) {
resultArr.push(data);
count++;
if (count === promiseArr.length) {
return resolve(data)
}
}).catch((err) => {
reject(err)
})
}
})
}
Promise.race
race顾名思义就是赛跑的意思,只要是第一个得到的结果,不管是成功还是失败,直接返回。
Promise.prototype.race = function(promiseArr) {
if (!Array.isArray(promiseArr)) {
throw new Error('argument must be an array');
};
return new Promise(function(resolve, reject) {
for(let i = 0; i < promiseArr.length; i++) {
promiseArr[i].then(resolve, reject);
};
});
};
Promise.resolve
返回一个立刻成功的promise
加入别人提供一个方法,需要你传一个promise,但你只有一个普通值,就可以用到此方法,将普通值(string,number,Object等)转换为Promise对象。
Promise.prototype.resolve = function(value) {
return new Promise(function(resolve) {
resolve(value);
});
};
Promise.reject
与resolve相反,返回一个立即失败的promise
Promise.prototype.reject = function(reason) {
return new Promise(function(resolve, reject) {
reject(reason);
});
};
Promise.catch
catch原理:只传失败回调
Promise.prototype.catch = function(onRejected) {
this.then(null, onRejected);
};
Promise.retry
retry是报错尝试,当尝试超过一定次数以后才是真正的reject
Promise.prototype.retry = function(getData, times, delay) {
return new Promise(function(resolve, reject) {
function attemp() {
getData().then((data) => {
resolve(data);
}).catch((err) => {
if (times === 0) {
reject(err)
} else {
times--;
setTimeout(attemp, delay);
}
})
}
attemp();
})
}
Promise.finally
不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后边的then。
Promise.prototype.finally = function(cb) {
return this.then((value) => {
return Promise.resolve(cb()).then(() => {
return value;
});
}, (errot) => {
return Promise.resolve(cb()).then(() => {
throw error
})
})
}
附加:
Promise的构造函数是同步执行的。then 中的方法是异步执行的。
Promise 是微任务,setTimeout 是宏任务,同一个事件循环中,promise.then总是先于 setTimeout 执行。
Comments | NOTHING