实现一个自己的Promise库
在上一篇中我们介绍了整个js异步发展的过程,今天我们就来自己完成一个Promise库,可以参照Promise的实现规范
首先先搭一个最简单的Promise框架
function Promise(executor) { // executor是一个执行函数
let self = this;
self.status = 'pending';
self.value = undefined; // 默认成功的值
self.reason = undefined; // 默认失败的原因
function resolve(value){ // 成功状态
if(self.status === 'pending'){
self.status = 'resolved';
self.value = value;
}
}
function reject(reason){ // 失败状态
if(self.status === 'pending'){
self.status = 'rejected';
self.reason = reason;
}
}
executor(resolve,reject)
}
Promise.prototype.then = function(onFulfilled,onRjected){
let self = this;
if(self.status === 'resolved'){
onFulfilled(self.value);
}
if(self.status === 'rejected'){
onRjected(self.reason);
}
}
module.exports = Promise
这里可以看到我们给Promise对象定义了3种状态以及一个用来当做默认成功会传入的值value和失败会传入的原因reason,同时定义了resolve和reject方法,将它们传入executor,也就是我们在调用生成Promise对象时会传入的执行函数,里面放着我们需要调用的异步逻辑.
接着我们通过Promise的原型定义了一个then的公有方法,它会接受两个参数onFullfilled和onRejected,这两个参数其实就是两个函数,是我们用来定义成功或失败时所执行的逻辑.
但是这里的问题是当我们executor里执行的是异步逻辑时,如果我们调用then,其实status依旧是pending的状态也就根本不会调用到onFullfilled或onRejected,所以我们需要定义两个callback数组来存放我们在pending状态下调用过的onFullfilled和onRejected函数,同时,在resolve和reject函数中我们需要将对应的callback数组中的onFullfilled和onRejected依次取出调用.
所以让我们来看下改进之后的代码:
function Promise(executor) { // executor是一个执行函数
let self = this;
self.status = 'pending';
self.value = undefined; // 默认成功的值
self.reason = undefined; // 默认失败的原因
self.onResolvedCallbacks = []; // 存放then成功的回调
self.onRejectedCallbacks = []; // 存放then失败的回调
function resolve(value) { // 成功状态
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
function reject(reason) { // 失败状态
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
executor(resolve, reject)
}
Promise.prototype.then = function (onFulfilled, onRjected) {
let self = this;
if (self.status === 'resolved') {
onFulfilled(self.value);
}
if (self.status === 'rejected') {
onRjected(self.reason);
}
// 当调用then时可能没成功 也没失败
if (self.status === 'pending') {
// 此时没有resolve 也没有reject
self.onResolvedCallbacks.push(function () {
onFulfilled(self.value);
});
self.onRejectedCallbacks.push(function () {
onRjected(self.reason);
});
}
}
下一步,我们就要开始考虑如何支持链式调用的情况了,如前文所说,链式调用要求我们每次调用then之后都需要返回一个新的Promise对象.
所以在我们需要对onFullfilled和onRejected返回的结果进行判断分析来决定之后then调用时是走向成功还是失败。返回的值有多种情况,它可能是一个值,也可能是另一个Promise对象。所以让我们定义一个新的函数用来解析.
function resolvePromise(p2,x,resolve,reject){
// 有可能这里返回的x是别人的promise
// 尽可能允许其他乱写
if(p2===x){ //这里应该报一个类型错误,有问题
return reject(new TypeError('循环引用了'))
}
// 看x是不是一个promise,promise应该是一个对象
if(x!==null||(typeof x === 'object'||typeof x === 'function')){
// 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
try{ // {then:1}
let then = x.then;
if(typeof then === 'function'){
// 成功
then.call(x,function(y){
// y可能还是一个promise,在去解析直到返回的是一个普通值
resolvePromise(promise2,y,resolve,reject)
},function(err){ //失败
reject(err);
})
}else{
resolve(x)
}
}catch(e){
reject(e);
}
}else{ // 说明是一个普通值1
resolve(x); // 表示成功了
}
}
这里判断的逻辑是先判断返回值x是不是指向当前将会返回的Promise对象自己,如果是自己的话就变成是自己等待自己,那我们就直接抛出一个循环引用的错误,然后接着判断x是不是一个对象,如果不是就说明是普通值,我们直接返回成功resolve并传入值x,如果是对象的话并且有一个then的方法的话我们就当他是一个Promise对象,如果不是Promise对象的话我们也就直接返回成功resolve并传入对象x,如果是的话那我们就直接调用then方法去判定它的成功与失败,失败的话我们就直接调用reject,成功的话我们还需在判断成功的值是否为Promise对象,如果不是的话我们可以直接resolve,是的话则我们要继续递归调用resolvePromise方法去解析一直到获取一个值位置.
我们看一下目前为止的代码:
function Promise(executor) { // executor是一个执行函数
let self = this;
self.status = 'pending';
self.value = undefined; // 默认成功的值
self.reason = undefined; // 默认失败的原因
self.onResolvedCallbacks = []; // 存放then成功的回调
self.onRejectedCallbacks = []; // 存放then失败的回调
function resolve(value) { // 成功状态
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
function reject(reason) { // 失败状态
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
try {
executor(resolve, reject)
} catch (e) { // 捕获的时候发生异常,就直接失败了
reject(e);
}
}
function resolvePromise(p2,x,resolve,reject){
// 有可能这里返回的x是别人的promise
// 尽可能允许其他乱写
if(p2===x){ //这里应该报一个类型错误,有问题
return reject(new TypeError('循环引用了'))
}
// 看x是不是一个promise,promise应该是一个对象
if(x!==null||(typeof x === 'object'||typeof x === 'function')){
// 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
try{ // {then:1}
let then = x.then;
if(typeof then === 'function'){
// 成功
then.call(x,function(y){
// y可能还是一个promise,在去解析直到返回的是一个普通值
resolvePromise(promise2,y,resolve,reject)
},function(err){ //失败
reject(err);
})
}else{
resolve(x)
}
}catch(e){
reject(e);
}
}else{ // 说明是一个普通值1
resolve(x); // 表示成功了
}
}
Promise.prototype.then = function (onFulfilled, onRjected) {
let self = this;
let promise2; //返回的promise
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
// 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
// x可能是一个promise 也有可能是一个普通的值
let x = onFulfilled(self.value);
// x可能是别人promise,写一个方法统一处理
resolvePromise(promise2,x,resolve,reject);
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
let x = onRjected(self.reason);
resolvePromise(promise2,x,resolve,reject);
})
}
// 当调用then时可能没成功 也没失败
if (self.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
// 此时没有resolve 也没有reject
self.onResolvedCallbacks.push(function () {
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
});
self.onRejectedCallbacks.push(function () {
let x = onRjected(self.reason);
resolvePromise(promise2,x,resolve,reject);
});
})
}
return promise2;
}
module.exports = Promise
下一步,由于规范中要求返回的Promise对象中的onFullfilled和onRejected执行逻辑必须遵从是异步的,所以我们可以用一个setTimeout函数来包住执行逻辑
setTimeout(function(){
try{
let x = onFulfilled(self.value);
// x可能是别人promise,写一个方法统一处理
resolvePromise(promise2, x, resolve, reject);
}catch(e){
reject(e);
}
})
同时如果调用then时没有传入onFullfilled或onRejected的话我们可以定义一个默认的函数给它们,这样就可以解决到值穿透的问题,例如promise.then().then().then()这样的调用
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:function(value){
return value;
}
onRjected = typeof onRjected === 'function'?onRjected:function(err){
throw err;
}
另外一个问题就是有些Promise的实现可能可以既调用成功又失败,那么对于这种情况我们就需要专门处理,永远只处理第一个调用的
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
try { // {then:1}
let then = x.then;
if (typeof then === 'function') {
// 成功
then.call(x, function (y) {
if (called) return
called = true
// y可能还是一个promise,在去解析直到返回的是一个普通值
resolvePromise(promise2, y, resolve, reject)
}, function (err) { //失败
if (called) return
called = true
reject(err);
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
} else { // 说明是一个普通值1
resolve(x); // 表示成功了
}
以上我们就完成了一个满足规范的Promise实现了,最后的实现代码如下:
function Promise(executor) { // executor是一个执行函数
let self = this;
self.status = 'pending';
self.value = undefined; // 默认成功的值
self.reason = undefined; // 默认失败的原因
self.onResolvedCallbacks = []; // 存放then成功的回调
self.onRejectedCallbacks = []; // 存放then失败的回调
function resolve(value) { // 成功状态
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
function reject(reason) { // 失败状态
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
try {
executor(resolve, reject)
} catch (e) { // 捕获的时候发生异常,就直接失败了
reject(e);
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 有可能这里返回的x是别人的promise
// 尽可能允许其他乱写
if (promise2 === x) { //这里应该报一个类型错误,有问题
return reject(new TypeError('循环引用了'))
}
// 看x是不是一个promise,promise应该是一个对象
let called; // 表示是否调用过成功或者失败
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
try { // {then:1}
let then = x.then;
if (typeof then === 'function') {
// 成功
then.call(x, function (y) {
if (called) return
called = true
// y可能还是一个promise,在去解析直到返回的是一个普通值
resolvePromise(promise2, y, resolve, reject)
}, function (err) { //失败
if (called) return
called = true
reject(err);
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
} else { // 说明是一个普通值1
resolve(x); // 表示成功了
}
}
Promise.prototype.then = function (onFulfilled, onRjected) {
//成功和失败默认不穿给一个函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
throw err;
}
let self = this;
let promise2; //返回的promise
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
// 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
// x可能是一个promise 也有可能是一个普通的值
setTimeout(function () {
try {
let x = onFulfilled(self.value);
// x可能是别人promise,写一个方法统一处理
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
// 当调用then时可能没成功 也没失败
if (self.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
// 此时没有resolve 也没有reject
self.onResolvedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
})
}
return promise2;
}
module.exports = Promise;
对于以上代码实现,我们可以通过一个promises-aplus-tests的库来验证是否达成规范要求
可以通过npm install -g promises-aplus-tests,使用时直接promises-aplus-tests 文件名即可
接下来让我们来实现一个类似于之前介绍的Q库中defer的语法糖,首先我们看一下以下这段代码:
function read() {
return new Promise(function(resolve,reject) {
require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) {
if(err) reject(err);
resolve(data);
})
})
}
read().then(function (data) {
console.log(data)
},function(err){
console.log(err);
})
这里我们封装了一个read函数,并返回一个Promise对象,但是可以看到这里我们必须在异步逻辑外面包一层Promise.我们可以通过定义Promise.defer的这样一个语法糖来简化这一层代码.
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
let Promise = require('./Promise');
function read() {
let defer = Promise.defer()
require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) {
if(err) defer.reject(err);
defer.resolve(data);
})
return defer.promise;
}
read().then(function (data) {
console.log(data)
},function(err){
console.log(err);
})
同理包括catch,all,race,resolve,reject我们都可以自己实现,这里就不多加赘述,直接上最终的代码:
function Promise(executor) { // executor是一个执行函数
let self = this;
self.status = 'pending';
self.value = undefined; // 默认成功的值
self.reason = undefined; // 默认失败的原因
self.onResolvedCallbacks = []; // 存放then成功的回调
self.onRejectedCallbacks = []; // 存放then失败的回调
function resolve(value) { // 成功状态
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
function reject(reason) { // 失败状态
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
try {
executor(resolve, reject)
} catch (e) { // 捕获的时候发生异常,就直接失败了
reject(e);
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 有可能这里返回的x是别人的promise
// 尽可能允许其他乱写
if (promise2 === x) { //这里应该报一个类型错误,有问题
return reject(new TypeError('循环引用了'))
}
// 看x是不是一个promise,promise应该是一个对象
let called; // 表示是否调用过成功或者失败
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
try { // {then:1}
let then = x.then;
if (typeof then === 'function') {
// 成功
then.call(x, function (y) {
if (called) return
called = true
// y可能还是一个promise,在去解析直到返回的是一个普通值
resolvePromise(promise2, y, resolve, reject)
}, function (err) { //失败
if (called) return
called = true
reject(err);
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
} else { // 说明是一个普通值1
resolve(x); // 表示成功了
}
}
Promise.prototype.then = function (onFulfilled, onRjected) {
//成功和失败默认不穿给一个函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
throw err;
}
let self = this;
let promise2; //返回的promise
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
// 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
// x可能是一个promise 也有可能是一个普通的值
setTimeout(function () {
try {
let x = onFulfilled(self.value);
// x可能是别人promise,写一个方法统一处理
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
// 当调用then时可能没成功 也没失败
if (self.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
// 此时没有resolve 也没有reject
self.onResolvedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
})
}
return promise2;
}
// 捕获错误的方法
Promise.prototype.catch = function (callback) {
return this.then(null, callback)
}
// 解析全部方法
// let arr = [];
// arr[1] = 100;
// console.log(arr.length)
Promise.all = function (promises) {
//promises是一个promise的数组
return new Promise(function (resolve, reject) {
let arr = []; //arr是最终返回值的结果
let i = 0; // 表示成功了多少次
function processData(index, y) {
arr[index] = y;
if (++i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (y) {
processData(i, y)
}, reject)
}
})
}
// 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
Promise.race = function (promises) {
return new Promise(function (resolve, reject) {
for (var i = 0; i < promises.length; i++) {
promises[i].then(resolve,reject)
}
})
}
// 生成一个成功的promise
Promise.resolve = function(value){
return new Promise(function(resolve,reject){
resolve(value);
})
}
// 生成一个失败的promise
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
})
}
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
module.exports = Promise;