Promise雛形(加入狀態機)
function Promise(executor){
let self = this;
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
function resolve(value){
if( self.status === 'pending'){
self.status = 'fulfilled';
self.value = value;
}
}
function reject(reason){
if( self.status === 'pending'){
self.status = 'rejected';
self.reason = reason;
}
}
executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
let self = this;
if(self.status === 'fulfilled'){
onFulfilled(self.value);
}
if(self.status === 'rejected'){
onRejected(self.reason);
}
}
module.exports = Promise;
複製程式碼
測試用例1
let Promise = require('Mypromise');
let promise = newPromise(function(resolve,reject){
resolve(results)
})
promise.then(function(data){
console.log(data);
},function(err){
console.log(err);
})
複製程式碼
首先我們定義一個status狀態變數,預設Promise處於pending狀態
promise接收一個executor函式,該函式在構造promise例項時就執行,所以在Promise構造方法中執行executor,executor需要接收resolve作為執行成功時的回撥函式,接收reject作為執行失敗時的回撥函式,所以定義了resolve和reject方法
resolve接收執行成功的返回結果作為引數
reject 接收執行失敗的返回結果作為引數
所以這裡定義了value表示成功結果,reason表示錯誤原因
呼叫resolve或reject後,會讓promise進入filfilled成功狀態或rejected失敗狀態,並且只有promise為在pending狀態下,才能切換到成功/失敗態
promise例項需要用then方法註冊執行成功/失敗的回撥方法,then中根據promise所處狀態,判斷呼叫成功還是失敗的回撥
這樣一個簡單的promise的實現就寫好了
加入非同步回撥處理,支援註冊多個then
function Promise(executor){
let self = this;
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value){
if( self.status === 'pending'){
self.status = 'fulfilled';
self.value = value;
self.onResolvedCallbacks.forEach(function(fn){
fn();
})
}
}
function reject(reason){
if( self.status === 'pending'){//只能從pending狀態切換到rejected狀態
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function(fn){
fn();
})
}
}
executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
let self = this;
if(self.status === 'fulfilled'){
onFulfilled(self.value);
}
if(self.status === 'rejected'){
onRejected(self.reason);
}
if(self.status === 'pending'){
self.onResolvedCallbacks.push( function(){
onFulfilled(self.value)
});
self.onRejectedCallbacks.push( function(){
onRejected(self.reason)
});
}
}
module.exports = Promise;
複製程式碼
測試用例2
let promise = new Promise(function(resolve,reject){
http.get(url, function(results) {
resolve(results)
})
})
promise.then(function(data){
console.log('data',data);
},function(err){
console.log('err',err);
})
promise.then(function(data){
console.log('data',data);
},function(err){
console.log('err',err);
})
promise.then(function(data){
console.log('data',data);
},function(err){
console.log('err',err);
})
複製程式碼
promise主要用於處理非同步回撥的情況,上例中發起http請求,請求成功後,用resolve發起成功的回撥;並呼叫多次then註冊了多個成功、失敗的回撥方法;如果執行成功,原生的promise會將每個then的成功回撥都執行一遍
由於非同步的請求,當呼叫then時,promise還處於pending狀態,所以我們需要將then註冊的回撥方法暫存,以便成功或失敗時回撥,為此定義 onResolvedCallbacks 、onRejectedCallbacks 分別存放then註冊的成功、失敗回撥方法,上例所示,
可能多次呼叫then註冊,所以onResolvedCallbacks =[],是個陣列
當執行成功會呼叫resolve,那我們在實現resolve方法時,將所有then中的成功回撥都呼叫一遍,就是這段程式碼
self.onResolvedCallbacks.forEach(function(fn){
fn();
})
複製程式碼
executor異常處理
當執行非同步操作時有可能發生異常,需要try/catch捕獲到異常,並使promise進入rejected狀態
try {
executor(resolve,reject); //捕獲的時候發生異常,執行reject
} catch (error) {
reject(error)
}
複製程式碼
可以在executor中丟擲throw new Error('error')測試
鏈式回撥的異常處理
then中無論是執行成功的回撥還是失敗回撥,只要有返回結果,都會走到下一個then(根據不同返回結果進入下一個then的不同回撥,規則我的另一偏文章juejin.im/post/5aae65…
promise是通過then中返回新的promise來實現鏈式呼叫的,試想:一個新的promise是可以繼續呼叫then方法的,補充then方法如下
Promise.prototype.then = function(onFulfilled,onRejected){
let self = this;
let promise2; //then返回的新Promise
if(self.status === 'fulfilled'){
//onFilfilled會同步執行,並返回新的promise2
promise2 = new Promise(function (resolve,reject) {
onFulfilled(self.value);
});
}
if(self.status === 'rejected'){
promise2 = new Promise(function (resolve,reject) {
onRejected(self.reason);
});
}
if(self.status === 'pending'){
promise2 = new Promise(function (resolve,reject) {
self.onResolvedCallbacks.push( function(){
//捕獲非同步回撥時的異常,如果有進入promise2的失敗態
try{
onFulfilled(self.value);
}catch (e){
reject(e);
}
});
self.onRejectedCallbacks.push( function(){
try{
onRejected(self.reason);
}catch (e){
reject(e);
}
});
});
}
return promise2;
}
複製程式碼
上面程式碼第一個if語句,執行成功回撥時,返回新的promise2,因為new promise時,executor會立即執行,所以onFulfilled(成功回撥)方法會同步執行,並捕獲其中的異常;第二個if語句失敗回撥同理。
第三個if語句,如果是非同步回撥,執行promise2的executor方法時,只是將這個成功的回撥onFulfilled加入成功回撥佇列onResolvedCallbacks(失敗同理),當成功回撥真正執行時,如果發生異常,還需要捕獲,並進入新promise2的失敗態
鏈式回撥的返回值處理
如果第一個promise返回一個普通值,會走到下一次then的成功的回撥
如果第一個promise返回了一個promise,需要等待返回的promise執行後的結果,再傳遞給下一次then中。
所以我們用x接收第一個then的返回值 let x = onFulfilled(self.value);
x可能是普通值,也可能是promise,也可能是別人實現的promise, 這裡實現一個resolvePromise方法統一處理返回值
then的程式碼更新如下 : 都用x接收回撥函式的返回值,並呼叫resolvePromise來處理
Promise.prototype.then = function(onFulfilled,onRejected){
let self = this;
let promise2;//then返回的新Promise
if(self.status === 'fulfilled'){
promise2 = new Promise(function (resolve,reject) {
let x= onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
});
}
if(self.status === 'rejected'){
promise2 = new Promise(function (resolve,reject) {
let x= onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
});
}
if(self.status === 'pending'){
promise2 = new Promise(function (resolve,reject) {
self.onResolvedCallbacks.push( function(){
try{
let x= onFulfilled(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);
}
});
});
}
return promise2;
}
複製程式碼
resolvePromise的實現如下
引數:
p2 :第二次then的promise例項
x:第一次then的返回值
resolve/reject : p2的 resolve/reject
function resolvePromise(p2,x,resolve,reject){
if(p2 === x){ //報一個型別錯誤
return reject(new TypeError('迴圈引用了'));
}
//判斷x是不是promise
if(x!== null || (typeof x === 'object'||typeof x === 'function')){
//x可能是promise 看物件中是否有then方法,有then就認為是promise
//取then方法有可能異常,發生異常就進入p2的失敗態
try {
let then = x.then;
if(typeof then === 'function'){
//認為x是一個promise,立刻執行該promise的then方法
//如果x進入成功態,會觸發成功回撥
then.call(x,function(y){
//y可能還是一個promise,繼續解析,直到返回的是一個普通值
resolvePromise(p2,y,resolve,reject);
},function(err){ //如果x進入失敗態,會觸發p2的失敗態
reject(err);
});
}else{ //如果then不是方法,直接認為返回一個物件,呼叫p2成功態
resolve(x);
}
} catch (error) {
reject(error);
}
}else{ //x是普通值,呼叫p2成功態
resolve(x);
}
};
複製程式碼
多次呼叫resolve,reject 加入called標識
如果有人把程式碼寫成
let p1= new Promise(function (resolve,reject) {
resolve('success');
reject('fail1');
});
複製程式碼
promise的處理方式是,一旦進入成功態,就成功了,不會再呼叫reject,反之亦然
這裡通過在resolvePromise方法中,加入called 標識,表示已經進入一個resolve或reject;若果called為true,直接返回,如果為false,置為true
function resolvePromise(p2, x, resolve, reject) {
if (p2 === x) { //這裡應該報一個型別錯誤,有問題
return reject(new TypeError('迴圈引用了'))
}
let called; // 表示是否呼叫過成功或者失敗
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function (y) {
if (called) return;
called = true;
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中值的穿透
我們可以在then中什麼都不寫
p1.then().then().then(function(data){
console.log('data',data);
},function(err){
console.log('err',err);
})
複製程式碼
p1的執行結果依然會穿透到最後一個then的相應的回撥
這需要在then方法中一開始就判斷,是否有resolve/reject方法,如果沒有,需要給預設的處理方法
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled :
function(value){ //預設的成功回撥,返回一個值,就會進入下一個then的成功回撥
return value;
};
onRejected = typeof onRejected === 'function' ? onRejected :
function(err){//預設的失敗回撥,丟擲異常,就會進入下一個then的失敗回撥
throw err;
};
複製程式碼
實現catch
捕獲錯誤的方法,catch就相當於一個then呼叫錯誤方法
Promise.prototype.catch = function (callback) {
return this.then(null,callback);
}
複製程式碼
實現Promise.all
all接收一個成員為promise例項的陣列,依次執行,並按順序返回執行結果
當所有promise都執行成功,就進入成功態,有一個執行失敗了,就進入失敗態
//promises是一個promise的陣列
Promise.all = function (promises) {
return new Promise(function (resolve, reject) {
let arr = []; //arr是最終返回值的結果
let count = 0; // 表示成功了多少次
function processData(index, y) {
arr[index] = y;
if (++count === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (y) {
processData(i, y)
}, reject) //有一個失敗,就呼叫失敗回撥
}
})
};
複製程式碼
實現Promise.race
引數同all,只要有一個promise成功了,就算成功。如果有一個失敗了,就失敗了,其他promise繼續執行
Promise.race = function (promises) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve,reject)
}
})
};
複製程式碼
實現Promise.resolve/Promise.reject
Promise.resolve 可以理解為 生成一個成功的promise
Promise.resolve = function(value){
return new Promise(function(resolve,reject){
resolve(value);
})
}
複製程式碼
Promise.reject 即生成一個失敗的promise
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
})
}
複製程式碼
Promises/A+規範明確要求回撥需要通過非同步方式執行,用以保證一致可靠的執行順序
這裡用setTimeout模擬非同步執行,將所有成功和失敗的回撥都用setTimeout包裝,既然非同步執行,還需捕獲異常
最後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;
}
// 捕獲錯誤的方法
Promise.prototype.catch = function (callback) {
return this.then(null, callback)
}
// 解析全部方法
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;
複製程式碼