為了更好的瞭解promise實現,我們一步步來完善promise庫。我們先來寫一個基礎用例,通過用例來看promise是如何實現的!
1.實現Promise基本方法
let Promise = require('./Promise');
// Promise是一個類,需要傳遞一個函式,這個函式我們稱之為執行函式,函式中有兩個引數resolve和reject他們也是函式,呼叫resolve表示成功,呼叫reject表示失敗
let promise = new Promise(function(resolve,reject){
// 成功就不會再呼叫失敗,預設狀態是等待狀態
resolve('ok');
reject('faild');
});
// then是原型上的一個方法接收兩個引數分別是成功的回撥和失敗的回撥
promise.then(function(data){// 呼叫resolve後會執行成功的回撥,呼叫reject後會執行失敗的回撥
console.log(data);
},function(err){
console.log(err);
});
複製程式碼
實現對應的Promise庫程式碼
function Promise(executor) {
// Promise中需要接收一個執行函式
let self = this;
self.status = 'pending'; //預設是pending狀態
self.value = undefined; // 成功的原因
self.reason = undefined; // 失敗的原因
function resolve(value) { // 呼叫resolve 會傳入為什麼成功
if(self.status === 'pending'){ // 只有再pending才能轉換成功態
self.value = value; // 將成功的原因儲存下來
self.status = 'resolved'; // 狀態改成成功態
}
}
function reject(reason) { // 呼叫reject會傳入為什麼失敗
if(self.status === 'pending'){
self.reason = reason;
self.status = 'rejected';
}
}
try {
executor(resolve, reject);// executor中需要傳入resolve和reject
} catch (e) {
// 如果executor執行發生異常,表示當前的promise是失敗態
reject(e);
}
}
// then中要傳入成功的回撥和失敗的回撥
Promise.prototype.then = function(onFufilled,onRejected){
let self = this;
// 如果要是成功就呼叫成功的回撥,並將成功的值傳入
if(self.status === 'resolved'){
onFufilled(self.value);
}
if(self.status === 'rejected'){
onRejected(self.reason);
}
}
module.exports = Promise
複製程式碼
2.非同步Promise
在new Promise時內部可以寫非同步程式碼,並且產生的例項可以then多次
let Promise = require('./Promise');
let promise = new Promise(function(resolve,reject){
setTimeout(function(){
resolve('ok');
},1000)
});
// 當呼叫then時可能狀態依然是pending狀態,我們需要將then中的回撥函式保留起來,當呼叫resolve或者reject時按照順序執行
promise.then(function(data){
console.log(data);
},function(err){
console.log(err);
});
promise.then(function(data){
console.log(data);
},function(err){
console.log(err);
});
複製程式碼
實現對應的Promise庫程式碼
self.status = 'pending'; //預設是pending狀態
self.value = undefined; // 成功的原因
self.reason = undefined; // 失敗的原因
+ self.onResolvedCallbacks = []; // 成功回撥存放的地方
+ self.onRejectedCallbacks = [];
function resolve(value) { // 呼叫resolve 會傳入為什麼成功
if(self.status === 'pending'){ // 只有再pending才能轉換成功態
self.value = value; // 將成功的原因儲存下來
self.status = 'resolved'; // 狀態改成成功態
+ // 依次執行成功的回撥
+ self.onResolvedCallbacks.forEach(item=>item());
}
}
function reject(reason) { // 呼叫reject會傳入為什麼失敗
if(self.status === 'pending'){
self.reason = reason;
self.status = 'rejected';
+ self.onRejectedCallbacks.forEach(item=>item());
}
}
Promise.prototype.then = function(onFufilled,onRejected){
if(self.status === 'rejected'){
onRejected(self.reason);
}
+ if(self.status === 'pending'){
+ // 如果是等待態,就將成功和失敗的回撥放到陣列中
+ self.onResolvedCallbacks.push(function(){
+ onFufilled(self.value);
+ });
+ self.onRejectedCallbacks.push(function(){
+ onRejected(self.reason);
+ });
+ }
}
module.exports = Promise
複製程式碼
3.Promise鏈式呼叫
promise實現鏈式呼叫,返回的並不是this而是一個新的promise,如果當前promise已經進入成功了的回撥,回撥中發生了異常如果返回的仍是當前的promise那麼狀態無法更改到失敗態!
promise.then(function(data){
throw Error('出錯了');// 當前promise已經成功了
return 'jw'
}).then(null,function(err){ // 如果返回的是同一個promise那麼還怎麼走向失敗呢?所以必須要返回一個新的promise
console.log(err);
})
複製程式碼
let Promise = require('./Promise');
let promise = new Promise(function(resolve,reject){
setTimeout(function(){
resolve('ok');
},1000)
});
promise.then(function(data){
// 如果返回的是一個普通的值,會將結果傳入下一次then的成功回撥中
// 如果發生錯誤會被下一次then的失敗回撥捕獲
// 如果返回的是promise看這個promise是成功還是失敗,對應呼叫下一次的then
return data+',no problem';
},function(err){
console.log(err);
}).then(function(data){
console.log(data);
},function(err){
console.log(err);
});
複製程式碼
實現對應的Promise庫程式碼
Promise.prototype.then = function(onFufilled,onRejected){
let self = this;
+ let promise2; // promise2為then呼叫後返回的新promise
// 如果要是成功就呼叫成功的回撥,並將成功的值傳入
if(self.status === 'resolved'){
- onFufilled(self.value);
+ promise2 = new Promise(function(resolve,reject){
+ try{
+ // 執行時有異常發生,需要將promise2的狀態置為失敗態
+ let x = onFufilled(self.value);
+ // x為返回的結果
+ // resolvePromise是對當前返回值進行解析,通過解析讓promise2的狀態轉化成成功態還是失敗態
+ 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;
}
複製程式碼
3.1 resolvePromise
resolvePromise是promise中最重要的方法,用來解析then返回的結果
function resolvePromise(promise2, x, resolve, reject) {
// 如果返回的promise和then中返回的promise是同一個promise,根據規範要報型別錯誤
// 相當於自己等待自己完成這是不科學的
if (promise2 === x) {
return reject(new TypeError('迴圈引用'))
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 如果是物件可能是一個thenable(帶有then方法)物件
let called; // 成功或者失敗不能同時呼叫
try { // 如果用defineProperty定義的then方法獲取時可能會有異常
let then = x.then;
// 如果then是函式,說明是promise,我們要讓promse執行
if (typeof then === 'function') {
then.call(x, function (y) {
if (called) return;
called = true;
// 如果resolve的結果依舊是promise那就繼續解析
resolvePromise(promise2, y, resolve, reject);
}, function (err) {
if (called) return;
called = true;
reject(err);
})
} else {
// 不是函式,x就是一個普通的物件,直接成功即可
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 是普通值直接呼叫成功
resolve(x);
}
}
複製程式碼
4.then中的方法非同步執行
為了保證程式執行的一致性,規範中要求then中的方法必須在下一佇列中執行
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
- try {
- // 執行時有異常發生,需要將promise2的狀態置為失敗態
- let x = onFufilled(self.value);
- // x為返回的結果
- // resolvePromise是對當前返回值進行解析,通過解析讓promise2的狀態轉化成成功態還是失敗態
- resolvePromise(promise2, x, resolve, reject);
- } catch (e) {
- reject(e);
- }
+ setTimeout(function(){
+ try {
+ // 執行時有異常發生,需要將promise2的狀態置為失敗態
+ let x = onFufilled(self.value);
+ // x為返回的結果
+ // resolvePromise是對當前返回值進行解析,通過解析讓promise2的狀態轉化成成功態還是失敗態
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+ }
+ })
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
- try {
- let x = onRejected(self.reason);
- resolvePromise(promise2, x, resolve, reject);
- } catch (e) {
- reject(e)
- }
+ setTimeout(function(){
+ 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 () {
- try {
- let x = onFufilled(self.value);
- resolvePromise(promise2, x, resolve, reject)
- } catch (e) {
- reject(e);
- }
+ setTimeout(function(){
+ try {
+ let x = onFufilled(self.value);
+ resolvePromise(promise2, x, resolve, reject)
+ } catch (e) {
+ reject(e);
+ }
+ })
});
self.onRejectedCallbacks.push(function () {
- try {
- let x = onRejected(self.reason);
- resolvePromise(promise2, x, resolve, reject)
- } catch (e) {
- reject(e);
- }
+ setTimeout(function(){
+ try {
+ let x = onRejected(self.reason);
+ resolvePromise(promise2, x, resolve, reject)
+ } catch (e) {
+ reject(e);
+ }
+ })
})
});
}
複製程式碼
5.值的穿透
在規範中定義then函式可以不傳參,不傳參預設會將成功的結果和失敗的結果繼續向下傳遞
Promise.prototype.then = function (onFufilled, onRejected) {
+ onFufilled = typeof onFufilled === 'function'?onFufilled:function(value){
+ return value
+ }
+ onRejected = typeof onRejected === 'function'?onRejected:function(err){
+ throw err
+ }
// 這裡我們可以順便加上catch方法
Promise.prototype.catch = function(callback){
return this.then(null,callback)
}
複製程式碼
6.Promise中的延遲物件
Promise.defer是Promise的語法糖
Promise.deferred = Promise.defer = function(){
let dfd = {};
dfd.promise = new Promise(function(resolve,reject){
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd
}
複製程式碼
看我們可以這樣來使用promise了
function defer(){
let defer = Promise.defer();
setTimeout(function(){
defer.resolve('ok')
},3000);
return defer.promise
}
defer().then(function(data){
console.log(data);
})
複製程式碼
7.resolve接收promise
let Promise = require('./3.async');
new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(new Promise(function (resolve, reject) {
resolve('ok');
}))
}, 2000)
}).then(function (data) {
console.log(data);
});
複製程式碼
對resolve方法進行改寫
function resolve(value) { // 呼叫resolve 會傳入為什麼成功
+ if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
+ if(typeof value.then === 'function'){
+ // 將當前promise成功的結果再次傳回resolve函式中
+ return value.then(resolve,reject);
+ }
+ }
if (self.status === 'pending') {
複製程式碼
8.promise測試
我們可以用promises-aplus-tests庫來測試我們的promise是否符合promiseA+規範
$ npm install promises-aplus-tests -g
promises-aplus-tests Promise.js
複製程式碼
9.Promise.all
Promise.all = function(promises){
let i = 0; // 用來計數有多少個promise執行成功
let result = [];// 存放結果
return new Promise(function(resolve,reject){
promises.forEach(function(p,index){
p.then(function(data){
// 將結果和陣列的索引對應起來
result[index] = data;
if(++i === promises.length){
// 當promise全部完成後呼叫all方法成功的回撥
resolve(result)
}
},reject);
});
});
}
複製程式碼
10.Promose.race
任何一個promise成功就變為成功態,失敗就變為失敗態
Promise.race = function (promises) {
return new Promise(function (resolve, reject) {
promises.forEach(function (p, index) {
p.then(function (data) {
resolve(data)
}, reject);
});
});
}
複製程式碼
11.Promise.resolve
返回一個成功的promise
Promise.resolve = function(value){
return new Promise(function(resolve,reject){
resolve(value);
});
}
複製程式碼
12.Promise.reject
返回一個失敗的promise
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
});
}
複製程式碼
就此我們實現了Promise的原理,喜歡的點個贊吧^_^!
支援我的可以給我打賞哈!