Promise本意是承諾,在程序中的意思就是承諾我過一段時間后會給你一個結(jié)果镶摘。
ES6 中采用了 Promise/A+ 規(guī)范盲憎,Promise 實(shí)現(xiàn)之前纫版,當(dāng)然要先了解 Promise/A+ 規(guī)范凿菩,規(guī)范地址https://promisesaplus.com/机杜。
我們根據(jù) Promise/A+ 規(guī)范,可以寫一個簡單的Promise庫衅谷。
每一步都盡量寫的詳細(xì)椒拗,所以代碼很長很羅嗦。
1.實(shí)現(xiàn)Promise基本的方法
- Promise是一個類获黔,需要傳遞一個executor函數(shù)蚀苛,函數(shù)里有兩個參數(shù)resolve和reject,調(diào)用resolve代表成功玷氏,調(diào)用reject代表失敗
- Promise有三種狀態(tài):默認(rèn)狀態(tài)是等待狀態(tài)pending堵未,成功resolved,失敗rejected
使用例子
let Promise = require('./Promise');
// Promise是一個類,需要傳遞一個executor函數(shù),這個函數(shù)我們稱之為執(zhí)行函數(shù),函數(shù)中有兩個參數(shù)resolve和reject他們也是函數(shù)盏触,調(diào)用resolve表示成功渗蟹,調(diào)用reject表示失敗
let promise = new Promise(function(resolve,reject){
// 成功就不會再調(diào)用失敗,默認(rèn)狀態(tài)是等待狀態(tài)pending
resolve('ok');
reject('faild');
});
// then是原型上的一個方法接收兩個參數(shù)分別是成功的回調(diào)和失敗的回調(diào)
promise.then(function(data){// 調(diào)用resolve后會執(zhí)行成功的回調(diào),調(diào)用reject后會執(zhí)行失敗的回調(diào)
console.log(data);
},function(err){
console.log(err);
});
實(shí)現(xiàn)對應(yīng)的Promise庫代碼
function Promise(executor) { // Promise中需要接收一個執(zhí)行函數(shù)executor
let self = this;
self.status = 'pending'; //默認(rèn)是pending狀態(tài)
self.value = undefined; // 成功的原因
self.reason = undefined; // 失敗的原因
function resolve(value) { // 調(diào)用resolve 會傳入為什么成功
if(self.status === 'pending'){ // 只有再pending才能轉(zhuǎn)換成功態(tài)
self.value = value; // 將成功的原因保存下來
self.status = 'resolved'; // 狀態(tài)改成成功態(tài)
}
}
function reject(reason) { // 調(diào)用reject會傳入為什么失敗
if(self.status === 'pending'){
self.reason = reason;
self.status = 'rejected';
}
}
try {
executor(resolve, reject);// executor中需要傳入resolve和reject
} catch (e) {
// 如果executor執(zhí)行發(fā)生異常赞辩,表示當(dāng)前的promise是失敗態(tài)
reject(e);
}
}
// then中要傳入成功的回調(diào)和失敗的回調(diào)
Promise.prototype.then = function(onFufilled,onRejected){
let self = this;
// 如果要是成功就調(diào)用成功的回調(diào),并將成功的值傳入
if(self.status === 'resolved'){
onFufilled(self.value);
}
if(self.status === 'rejected'){
onRejected(self.reason);
}
}
module.exports = Promise
2.異步Promise
在new Promise時內(nèi)部可以寫異步代碼拙徽,并且產(chǎn)生的實(shí)例可以then多次
- 用兩個數(shù)組存放成功和失敗的回調(diào),當(dāng)調(diào)用的時候再執(zhí)行
使用例子
let Promise = require('./Promise');
let promise = new Promise(function(resolve,reject){
setTimeout(function(){
resolve('ok');
},1000)
});
// 當(dāng)調(diào)用then時可能狀態(tài)依然是pending狀態(tài),我們需要將then中的回調(diào)函數(shù)保留起來,當(dāng)調(diào)用resolve或者reject時按照順序執(zhí)行
promise.then(function(data){
console.log(data);
},function(err){
console.log(err);
});
promise.then(function(data){
console.log(data);
},function(err){
console.log(err);
});
實(shí)現(xiàn)對應(yīng)的Promise庫代碼
function Promise(executor) {
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
+ self.onResolvedCallbacks = []; // 成功回調(diào)存放的地方
+ self.onRejectedCallbacks = [];// 失敗回調(diào)存放的地方
function resolve(value) {
if(self.status === 'pending'){
self.value = value;
self.status = 'resolved';
+ // 依次執(zhí)行成功的回調(diào)
+ self.onResolvedCallbacks.forEach(item=>item());
}
}
function reject(reason) {
if(self.status === 'pending'){
self.reason = reason;
self.status = 'rejected';
+ // 依次執(zhí)行失敗的回調(diào)
+ self.onRejectedCallbacks.forEach(item=>item());
}
}
}
Promise.prototype.then = function(onFufilled,onRejected){
if(self.status === 'rejected'){
onRejected(self.reason);
}
+ if(self.status === 'pending'){
+ // 如果是等待態(tài),就將成功和失敗的回調(diào)放到數(shù)組中
+ self.onResolvedCallbacks.push(function(){
+ onFufilled(self.value);
+ });
+ self.onRejectedCallbacks.push(function(){
+ onRejected(self.reason);
+ });
+ }
}
3.Promise鏈?zhǔn)秸{(diào)用
如果當(dāng)前promise已經(jīng)進(jìn)入成功的回調(diào)诗宣,回調(diào)中發(fā)生了異常返回this的話膘怕,那么當(dāng)前的promise的狀態(tài)無法更改到失敗臺!所以promise實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,返回的并不是this而是一個新的promise召庞。
-
執(zhí)行回調(diào)中
- 如果返回的是一個普通的值岛心,會將結(jié)果傳入下一次then的成功回調(diào)中
- 如果發(fā)生錯誤會被下一次then的失敗回調(diào)捕獲
- 如果返回的是promise看這個promise是成功還是失敗,對應(yīng)調(diào)用下一次的then
所以寫一個resolvePromise方法,這是promise中最重要的方法,用來解析then返回的結(jié)果
有些人可能成功失敗同時調(diào)用篮灼,如果兩個都調(diào)用忘古,用第一個調(diào)用的,不允許同時調(diào)用诅诱。
使用例子
promise.then(function(data){
throw Error('出錯了');// 當(dāng)前promise已經(jīng)成功了髓堪,成功就不會再失敗
return 'renee'
}).then(null,function(err){ // 如果返回的是同一個promise那么還怎么走向失敗呢?所以必須要返回一個新的promise
console.log(err);
})
實(shí)現(xiàn)對應(yīng)的Promise庫代碼
Promise.prototype.then = function(onFufilled,onRejected){
let self = this;
+ let promise2; // promise2為then調(diào)用后返回的新promise
// 如果要是成功就調(diào)用成功的回調(diào),并將成功的值傳入
if(self.status === 'resolved'){
- onFufilled(self.value);
+ promise2 = new Promise(function(resolve,reject){
+ try{
+ // 執(zhí)行時有異常發(fā)生,需要將promise2的狀態(tài)置為失敗態(tài)
+ let x = onFufilled(self.value);
+ // x為返回的結(jié)果
+ // 寫一個方法resolvePromise,是對當(dāng)前返回值進(jìn)行解析,通過解析讓promise2的狀態(tài)轉(zhuǎn)化成成功態(tài)還是失敗態(tài)
+ resolvePromise(promise2,x,resolve,reject);
+ }catch(e){
+ reject(e);
+ }
+ })
}
if(self.status === 'rejected'){
- onRejected(self.reason);
+ promise2 = new Promise(function(resolve,reject){
+ try{
+ let x = onRejected(self.reason);
+ resolvePromise(promise2,x,resolve,reject);
+ }catch(e){
+ reject(e)
+ }
+ })
}
if(self.status === 'pending'){
+ promise2 = new Promise(function(resolve,reject){
self.onResolvedCallbacks.push(function(){
- onFufilled(self.value);
+ try{
+ let x = onFufilled(self.value);
+ resolvePromise(promise2,x,resolve,reject)
+ }catch(e){
+ reject(e);
+ }
+ })
});
self.onRejectedCallbacks.push(function(){
- onRejected(self.reason);
+ try{
+ let x = onRejected(self.reason);
+ resolvePromise(promise2,x,resolve,reject)
+ }catch(e){
+ reject(e);
+ }
});
+ })
}
+ return promise2;
}
function resolvePromise(promise2, x, resolve, reject) {
//x是返回的結(jié)果娘荡,如果promise和then中返回的promise是同一個干旁,是不科學(xué)的,要報錯
if(promise2===x){
return reject(new Error('循環(huán)引用'))
}
if(x!==null&&(typeof x === 'object'|| typeof x === 'function')){
let called; //表示是否調(diào)用過成功或者失敗
try{
let then=x.then;
//如果then是函數(shù)炮沐,說明是promise争群,我們要讓promise執(zhí)行
if(typeof then==='function'){
then.call(x,function(y){
if(called)return; //如果調(diào)用過直接return
called=true;
//如果resolve的結(jié)果依舊是promise那就繼續(xù)解析
},function(err){
if(called) return;
called=true;
reject(err)
})
}else{//如果不是函數(shù),x是一個普通的對象大年,直接成功即可
resolve(x)
}
}catch(e){
if(called) return;
called=true;
reject(e);
}
}else{
//是普通值直接調(diào)用成功
resolve(x);
}
}
4.值的穿透
- 在規(guī)范中定義then函數(shù)可以不傳參,不傳參默認(rèn)會將成功的結(jié)果和失敗的結(jié)果繼續(xù)向下傳遞
使用例子
promise.then().then().then(function(data){
console.log(data)
})
實(shí)現(xiàn)對應(yīng)的Promise庫代碼
Promise.prototype.then = function (onFufilled, onRejected) {
//失敗和成功默認(rèn)不傳給一個函數(shù)
+ onFufilled = typeof onFufilled === 'function'?onFufilled:function(value){
+ return value
+ }
+ onRejected = typeof onRejected === 'function'?onRejected:function(err){
+ throw err
+ }
5.測試
另外可以通過安裝一個插件來對實(shí)現(xiàn)的promise進(jìn)行規(guī)范測試换薄。
npm(cnpm) i -g promises-aplus-tests
promises-aplus-tests 文件名