写了好几天,终于写完了一个符合Promise A+ 规范的promise了,并且跑通了测试用例,改天再仔细讲解吧....
一. 为什么
首先为什么要自己手动实现一个promise呢,因为在使用的过程中,你会发现网上各种文章在分析怎么用,各种奇淫技巧给你讲解promise的写法,一不小心你就落入了敌人的陷进,刚刚背下了这种写法,下一个更奇怪的写法让你怀疑人生,好吧,我怕了,自己实现一个还不行吗,当然网上也有实现的代码,但是还是得自己一行一行敲,这种真实感才会让你记忆尤新
二. 准备工作
Promise/A+规范: 首先可以看到一篇文章叫做Promise/A+规范官方文档, 这可谓是官方文档了,当时这只告诉你规范,怎么写代码是你的事,只要表现一样,能通过所有测试用例则行
什么是Promsie: 当然首先要看下Promise是什么 promise就是改善js回调地狱的一种代码结构,这种通过链式调用的方法一定程度上看着要好看些,也便于维护,当然最新的
async await
同样也事解决这个问题的,但是都是基于promise的原理,所以学这个很有必要怎么用Promise: 下面代码可以看到就是promise的简单用法,把一个
setTimeout
包裹成可以链式调用的Promise,就不用像之前那样写回调函数了,只需要then then then...
所以,then , 我们来写一个promise吧
/**
* setTimeout改成promise写法
*/
let Promise = require("./promise")
let p1 = new Promise(function (reslove, reject) {
setTimeout(function () {
let num = Math.random();
console.log(num);
if (num > 0.5) {
reslove("成功");
} else {
reject("失败")
}
}, 2000)
});
p1.then(function (data) {
console.log("1 resovle",data)
return data;
}, function (error) {
console.log("1 reject",error)
}).then(function(data) {
console.log("2 resolve",data)
},function(error){
console.log("2 reject",error)
})
三. Promise原理思路
这里写源码的方法就是对照这Promise/A+规范,按照规范一点一点的写,当然不出来可以参照人家写的源码,下面附上手写的基本思路,具体源码在下面
- 规范
2.1.Promise States
: 告诉我们一个promise有三种状态pending
,fulfiiled
和rejected
三种状态,通俗理解就是等待
,完成
和失败
, 等待态可以转换为后者,反之不行,这样就定义了三种状态如下
// 定义状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
创建Promise:上面可以看到,
new Promise(function (reslove, reject)
在new的时候传入了一个函数委托给promise, 这个函数会被马上调用,同时会再次把内部的resolve
和reject
函数传出来,委托你来调用,当你觉得成功时调用resolve
,失败时调用reject
,所以我们在promsie内部就要写两个函数resolve
和reject
,那么这两个函数内部干嘛呢,简单的说resolve
就是调用你在then
里面传入的第一个函数,reject
就是调用你在then
里面传入的第二个函数,由于可以写很多then,所以内部必须由一个数组来保存,然后依次遍历调用即可,这样你从resolve(data)
和reject(error)
传入的参数就可以被传递到then传入两个函数的回调中,这就是promise实现的基本原理:利用函数当作参数传递和委托调用来简化回调方式为链式调用方式规范
2.2.The then Method
: 告诉我们new 一个promise 返回的对象有then
方法,所以我们在promise的原型上面增加then方法,里面传入两个函数,在成功调用resolve
会被执行前者,失败调用reject
执行后者promise.then(onFulfilled, onRejected)
,那么then
内部干嘛呢,没错,把传入的两个函数保存到内部的数组里面呀,等到resolve和reject调用的时候确保可以循环调用,你就会问了,万一在then
之前就被resolve和reject了怎么办,都没有保存,没错,当你不知道怎么办的时候就用setTimeout
把resolve和reject内部代码包一下,放到后面延时执行,所谓事件循环宏任务队列,在当前执行栈执行完才会执行,好吧,这样就能确保then肯定先执行了,我们一个简单的promie就写好了
Promise.prototype.then = function (onFulfilled, onRejected) {
//... ... 省略细节,可以参考下面源码
}
规范
2.3.The Promise Resolution Procedure
: 当然上面实现的只是一个简单的promise, 注意我们then完了还要返回一个新的promise呀,好吧,所以在then中的返回值都要用new 一个新的promise这样包起来返回,更恶心的是,规范允许你在then的参数函数onFulfilled
和onRejected
中还继续返回promise怎么办, 我的老天, 这些个promise也要递归上面所有操作,所以要写一个函数来处理这一切,这就是function resolvePromise(promise2, x, resolve, reject)
,作用就是,判断是返回一个promise则递归调用resolvePromise来解析promise,直到最后不是promise则才调用resolve来完成此层promise,期间任何错误都调用reject的传递失败态好了,理解上面基本就理解了整个promise的原理,但是你看下面代码依然很吃力,因为牵扯函数参数,循环递归,状态切换,延时执行等,慢慢熟悉相信会更好的理解的,当你真正理解了,那么你的js水平肯定会更上一层楼,Jason zeng (* . *) 写这篇文章的目的也达到了呀,下面附上源码吧,哈哈!
四. 手写的Promise源码
/**
* 今天的目标就是写一个符合PromiseA+规范的promise
*
*/
// 定义状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(executor) {
let self = this;// 先缓存当前promise的实例
self.status = PENDING; // 设置状态
self.value = undefined; // fulfilled状态时 返回的信息
self.reason = undefined; // rejected状态时 拒绝的原因
self.onResolvedCallbacks = []; // 定义存放成功的回调的数组
self.onRejectedCallbacks = []; // 定义存放失败的回调的数组
// 成功时候执行的回调
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
setTimeout(() => {
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value;
self.onResolvedCallbacks.forEach(cb => cb(self.value));
}
});
}
function reject(reason) {
setTimeout(() => {
if (self.status === PENDING) {
self.status = REJECTED;
self.reason = reason;
self.onRejectedCallbacks.forEach(cb => cb(self.reason));
}
});
}
// 捕获在excutor执行器中抛出的异常
// new Promise((resolve, reject) => {
// throw new Error('error in excutor')
// })
try {
// 因为函数执行会出现异常,所以捕获异常并调用reject转为失败态
executor(resolve, reject);
} catch (e) {
// 用error来reject这个promise
reject(e);
}
}
/**
* resolve中的值几种情况:
* 1.普通值
* 2.promise对象
* 3.thenable对象/函数
*/
/**
* 对resolve 进行改造增强 针对resolve中不同值情况 进行处理
* @param {promise} promise2 promise1.then方法返回的新的promise对象
* @param {[type]} x promise1中onFulfilled的返回值
* @param {[type]} resolve promise2的resolve方法
* @param {[type]} reject promise2的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
// 如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错
if (promise2 === x) {
return reject(new TypeError("循环引用"));
}
let called = false; // 避免多次调用
// 如果x是一个promise对象 (该判断和下面 判断是不是thenable对象重复 所以可有可无)
if (x instanceof Promise) { // 获得它的终值 继续resolve
if (x.status === PENDING) { // 如果为等待态需等待直至 x 被执行或拒绝 并解析y值
x.then(y => {
resolvePromise(promise2, y, resolve, reject);
}, reason => {
reject(reason);
});
} else { // 如果 x 已经处于执行态/拒绝态(值已经被解析为普通值),用相同的值执行传递下去 promise
x.then(resolve, reject);
}
// 如果 x 为对象或者函数
} else if (x !== null && ((typeof x === "object") || (typeof x === "function"))) {
try { // 是否是thenable对象,具有then方法的对象或函数
let then = x.then;
if (typeof then === "function") { // 如果还有then则调用then
then.call(x, function (y) {
if (called) return;
called = true;
// 递归调用到最后为一个没有then的普通对象
resolvePromise(promise2, y, resolve, reject);
}, function (reason) {
if (called) return;
called = true;
reject(reason);
})
} else { // 说明是一个普通对象/函数, 则调用resolve来解决它
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else { // 如果返回不是thenable对象,直接解决它!
resolve(x);
}
}
/**
* [注册fulfilled状态/rejected状态对应的回调函数]
* @param {function} onFulfilled fulfilled状态时 执行的函数
* @param {function} onRejected rejected状态时 执行的函数
* @return {function} newPromsie 返回一个新的promise对象
*/
Promise.prototype.then = function (onFulfilled, onRejected) {
// 如果没有传参数,表示这个then没有逻辑,把结果往后抛,注意reject就是把错误继续throw
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason };
let self = this;
let promise2;
// ------------- executor内部是同步的时候,直接调用
// 如果当前promise的状态是成功态,onFulfilled直接取值
if (self.status === FULFILLED) {
return promise2 = new Promise(function (resolve, reject) {
setTimeout(() => {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
})
}
// 失败态,直接取值reject
if (self.status === REJECTED) {
return promise2 = new Promise(function (resolve, reject) {
setTimeout(() => {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
})
}
if (self.status === PENDING) { //等待态
return promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallbacks.push(function (value) {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
self.onRejectedCallbacks.push(function (reason) {
try {
let x = onRejected(reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
};
/**
* [Promise.all Promise进行并行处理]
* @param {Array} promises promise对象组成的数组作为参数
* @return {function} 返回一个Promise实例
*/
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
// let done = gen(promises.lenght, resolve);
let count = 0;
let values = []; // 由于promise.all([]) 这里传递的是数组,所以这里保留空数组
promises.forEach((promise, index) => {
promise.then(value => { // 调用then表示当他们完成后会传递到这里来
//done(value,index);
values[index] = value; // 保存所有完成的值
if (++count === promises.length) { // 当所有都完成的时候再解决它们
resolve(values);
}
}, reason => {
reject(reason);
})
})
});
};
/**
* [Promise.race Promise比赛,完成则继续走后面的]
* @param {Array} promises promise对象组成的数组作为参数
* @return {function} 返回一个Promise实例
*/
Promise.race = function (promises) {
return new Promise((reslove, reject) => {
promises.forEach((promise, index) => {
// 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理(取决于哪一个更快)
// 这里只是简单的传递,都给它们用then先绑定好成功或失败的函数,谁先成功或失败就会执行谁,这里就不用管了
promise.then(reslove, reject);
})
});
}
// 用于promise方法链时 捕获前面onFulfilled/onRejected抛出的异常
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
}
// 用promise包装下,等待自动调用
Promise.resolve = function (value) {
return new Promise(resolve => {
resolve(value);
});
}
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
/**
* 基于Promise实现Deferred的
* Deferred和Promise的关系
* - Deferred 拥有 Promise
* - Deferred 具备对 Promise的状态进行操作的特权方法(resolve reject)
*
*参考jQuery.Deferred
*url: http://api.jquery.com/category/deferred-object/
*/
Promise.deferred = function () { // 延迟对象
let defer = {};
defer.promise = new Promise((resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
}
try {
module.exports = Promise
} catch (e) {
}