promise then err_Promise 原理解析与实现(遵循Promise/A+规范)
Promise是JS異步編程中的重要概念,異步抽象處理對象,是目前比較流行Javascript異步編程解決方案之一
2對于幾種常見異步編程方案回調(diào)函數(shù)
事件監(jiān)聽
發(fā)布/訂閱
Promise對象
這里就拿回調(diào)函數(shù)說說
1.對于回調(diào)函數(shù) 我們用Jquery的ajax獲取數(shù)據(jù)時 都是以回調(diào)函數(shù)方式獲取的數(shù)據(jù)
$.get(url,?(data)?=>?{????console.log(data)
)
2.如果說 當我們需要發(fā)送多個異步請求 并且每個請求之間需要相互依賴 那這時 我們只能 以嵌套方式來解決 形成 “回調(diào)地獄”
$.get(url,?data1?=>?{????console.log(data1)
????$.get(data1.url,?data2?=>?{
????????console.log(data1)
????})
})
這樣一來,在處理越多的異步邏輯時,就需要越深的回調(diào)嵌套,這種編碼模式的問題主要有以下幾個:
代碼邏輯書寫順序與執(zhí)行順序不一致,不利于閱讀與維護。
異步操作的順序變更時,需要大規(guī)模的代碼重構(gòu)。
回調(diào)函數(shù)基本都是匿名函數(shù),bug 追蹤困難。
回調(diào)函數(shù)是被第三方庫代碼(如上例中的 ajax )而非自己的業(yè)務(wù)代碼所調(diào)用的,造成了 IoC 控制反轉(zhuǎn)。
Promise 處理多個相互關(guān)聯(lián)的異步請求
1.而我們Promise 可以更直觀的方式 來解決 “回調(diào)地獄”
const?request?=?url?=>?{?????return?new?Promise((resolve,?reject)?=>?{
????????$.get(url,?data?=>?{
????????????resolve(data)
????????});
????})
};
//?請求data1
request(url).then(data1?=>?{
????return?request(data1.url);???
}).then(data2?=>?{
????return?request(data2.url);
}).then(data3?=>?{
????console.log(data3);
}).catch(err?=>?throw?new?Error(err));
2.相信大家在 vue/react 都是用axios fetch 請求數(shù)據(jù) 也都支持 Promise API
import?axios?from?'axios';axios.get(url).then(data?=>?{
???console.log(data)
})
Axios 是一個基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。
3Promise使用1.Promise 是一個構(gòu)造函數(shù), new Promise 返回一個 promise對象 接收一個excutor執(zhí)行函數(shù)作為參數(shù), excutor有兩個函數(shù)類型形參resolve reject
const?promise?=?new?Promise((resolve,?reject)?=>?{???????//?異步處理
???????//?處理結(jié)束后、調(diào)用resolve?或?reject
});
2.promise相當于一個狀態(tài)機
promise的三種狀態(tài)
pending
fulfilled
rejected
1.promise 對象初始化狀態(tài)為 pending?
2.當調(diào)用resolve(成功),會由pending => fulfilled?
3.當調(diào)用reject(失敗),會由pending => rejected
注意promsie狀態(tài) 只能由 pending => fulfilled/rejected, 一旦修改就不能再變
3.promise對象方法
1.then方法注冊 當resolve(成功)/reject(失敗)的回調(diào)函數(shù)
//?onFulfilled?是用來接收promise成功的值//?onRejected?是用來接收promise失敗的原因
promise.then(onFulfilled,?onRejected);
then方法是異步執(zhí)行的
2.resolve(成功) onFulfilled會被調(diào)用
const?promise?=?new?Promise((resolve,?reject)?=>?{???resolve('fulfilled');?//?狀態(tài)由?pending?=>?fulfilled
});
promise.then(result?=>?{?//?onFulfilled
????console.log(result);?//?'fulfilled'?
},?reason?=>?{?//?onRejected?不會被調(diào)用
})
3.reject(失敗) onRejected會被調(diào)用
const?promise?=?new?Promise((resolve,?reject)?=>?{???reject('rejected');?//?狀態(tài)由?pending?=>?rejected
});
promise.then(result?=>?{?//?onFulfilled?不會被調(diào)用
},?reason?=>?{?//?onRejected?
????console.log(reason);?//?'rejected'
})
4.promise.catch
在鏈式寫法中可以捕獲前面then中發(fā)送的異常
promise.catch(onRejected)相當于
promise.then(null,?onRrejected);
//?注意
//?onRejected?不能捕獲當前onFulfilled中的異常
promise.then(onFulfilled,?onRrejected);?
//?可以寫成:
promise.then(onFulfilled)
???????.catch(onRrejected);???
4.Promise chain
promise.then方法每次調(diào)用 都返回一個新的promise對象 所以可以鏈式寫法
function?taskA()?{????console.log("Task?A");
}
function?taskB()?{
????console.log("Task?B");
}
function?onRejected(error)?{
????console.log("Catch?Error:?A?or?B",?error);
}
var?promise?=?Promise.resolve();
promise
????.then(taskA)
????.then(taskB)
????.catch(onRejected)?//?捕獲前面then方法中的異常?
5.Promise的靜態(tài)方法
Promise.resolve 返回一個fulfilled狀態(tài)的promise對象
????console.log(value);
});
Promise.resolve('hello');
//?相當于
const?promise?=?new?Promise(resolve?=>?{
???resolve('hello');
});
2.Promise.reject 返回一個rejected狀態(tài)的promise對象
Promise.reject(24);new?Promise((resolve,?reject)?=>?{
???reject(24);
});
3.Promise.all 接收一個promise對象數(shù)組為參數(shù)
只有全部為resolve才會調(diào)用 通常會用來處理 多個并行異步操作
const?p1?=?new?Promise((resolve,?reject)?=>?{????resolve(1);
});
const?p2?=?new?Promise((resolve,?reject)?=>?{
????resolve(2);
});
const?p3?=?new?Promise((resolve,?reject)?=>?{
????reject(3);
});
Promise.all([p1,?p2,?p3]).then(data?=>?{?
????console.log(data);?//?[1,?2,?3]?結(jié)果順序和promise實例數(shù)組順序是一致的
},?err?=>?{
????console.log(err);
});
4.Promise.race 接收一個promise對象數(shù)組為參數(shù)
Promise.race 只要有一個promise對象進入 FulFilled 或者 Rejected 狀態(tài)的話,就會繼續(xù)進行后面的處理。
function?timerPromisefy(delay)?{????return?new?Promise(function?(resolve,?reject)?{
????????setTimeout(function?()?{
????????????resolve(delay);
????????},?delay);
????});
}
var?startDate?=?Date.now();
Promise.race([
????timerPromisefy(10),
????timerPromisefy(20),
????timerPromisefy(30)
]).then(function?(values)?{
????console.log(values);?//?10
});
5.Promise的finally
Promise.prototype.finally?=?function?(callback)?{??let?P?=?this.constructor;
??return?this.then(
????value??=>?P.resolve(callback()).then(()?=>?value),
????reason?=>?P.resolve(callback()).then(()?=>?{?throw?reason?})
??);
};
4Promise代碼實現(xiàn)/**
?*?Promise?實現(xiàn)?遵循promise/A+規(guī)范
?*?Promise/A+規(guī)范譯文:
?*?https://malcolmyu.github.io/2015/06/12/Promises-A-Plus/#note-4
?*/
//?promise?三個狀態(tài)
const?PENDING?=?"pending";
const?FULFILLED?=?"fulfilled";
const?REJECTED?=?"rejected";
function?Promise(excutor)?{
????let?that?=?this;?//?緩存當前promise實例對象
????that.status?=?PENDING;?//?初始狀態(tài)
????that.value?=?undefined;?//?fulfilled狀態(tài)時?返回的信息
????that.reason?=?undefined;?//?rejected狀態(tài)時?拒絕的原因
????that.onFulfilledCallbacks?=?[];?//?存儲fulfilled狀態(tài)對應(yīng)的onFulfilled函數(shù)
????that.onRejectedCallbacks?=?[];?//?存儲rejected狀態(tài)對應(yīng)的onRejected函數(shù)
????function?resolve(value)?{?//?value成功態(tài)時接收的終值
????????if(value?instanceof?Promise)?{
????????????return?value.then(resolve,?reject);
????????}
????????//?為什么resolve?加setTimeout?
????????//?2.2.4規(guī)范?onFulfilled?和?onRejected?只允許在?execution?context?棧僅包含平臺代碼時運行.
????????//?注1?這里的平臺代碼指的是引擎、環(huán)境以及?promise?的實施代碼。實踐中要確保?onFulfilled?和?onRejected?方法異步執(zhí)行,且應(yīng)該在?then?方法被調(diào)用的那一輪事件循環(huán)之后的新執(zhí)行棧中執(zhí)行。
????????setTimeout(()?=>?{
????????????//?調(diào)用resolve?回調(diào)對應(yīng)onFulfilled函數(shù)
????????????if?(that.status?===?PENDING)?{
????????????????//?只能由pending狀態(tài)?=>?fulfilled狀態(tài)?(避免調(diào)用多次resolve?reject)
????????????????that.status?=?FULFILLED;
????????????????that.value?=?value;
????????????????that.onFulfilledCallbacks.forEach(cb?=>?cb(that.value));
????????????}
????????});
????}
????function?reject(reason)?{?//?reason失敗態(tài)時接收的拒因
????????setTimeout(()?=>?{
????????????//?調(diào)用reject?回調(diào)對應(yīng)onRejected函數(shù)
????????????if?(that.status?===?PENDING)?{
????????????????//?只能由pending狀態(tài)?=>?rejected狀態(tài)?(避免調(diào)用多次resolve?reject)
????????????????that.status?=?REJECTED;
????????????????that.reason?=?reason;
????????????????that.onRejectedCallbacks.forEach(cb?=>?cb(that.reason));
????????????}
????????});
????}
????//?捕獲在excutor執(zhí)行器中拋出的異常
????//?new?Promise((resolve,?reject)?=>?{
????//?????throw?new?Error('error?in?excutor')
????//?})
????try?{
????????excutor(resolve,?reject);
????}?catch?(e)?{
????????reject(e);
????}
}
/**
?*?resolve中的值幾種情況:
?*?1.普通值
?*?2.promise對象
?*?3.thenable對象/函數(shù)
?*/
/**
?*?對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)?{
????if?(promise2?===?x)?{??//?如果從onFulfilled中返回的x?就是promise2?就會導致循環(huán)引用報錯
????????return?reject(new?TypeError('循環(huán)引用'));
????}
????let?called?=?false;?//?避免多次調(diào)用
????//?如果x是一個promise對象?(該判斷和下面?判斷是不是thenable對象重復?所以可有可無)
????if?(x?instanceof?Promise)?{?//?獲得它的終值?繼續(xù)resolve
????????if?(x.status?===?PENDING)?{?//?如果為等待態(tài)需等待直至?x?被執(zhí)行或拒絕?并解析y值
????????????x.then(y?=>?{
????????????????resolvePromise(promise2,?y,?resolve,?reject);
????????????},?reason?=>?{
????????????????reject(reason);
????????????});
????????}?else?{?//?如果?x?已經(jīng)處于執(zhí)行態(tài)/拒絕態(tài)(值已經(jīng)被解析為普通值),用相同的值執(zhí)行傳遞下去?promise
????????????x.then(resolve,?reject);
????????}
????????//?如果?x?為對象或者函數(shù)
????}?else?if?(x?!=?null?&&?((typeof?x?===?'object')?||?(typeof?x?===?'function')))?{
????????try?{?//?是否是thenable對象(具有then方法的對象/函數(shù))
????????????let?then?=?x.then;
????????????if?(typeof?then?===?'function')?{
????????????????then.call(x,?y?=>?{
????????????????????if(called)?return;
????????????????????called?=?true;
????????????????????resolvePromise(promise2,?y,?resolve,?reject);
????????????????},?reason?=>?{
????????????????????if(called)?return;
????????????????????called?=?true;
????????????????????reject(reason);
????????????????})
????????????}?else?{?//?說明是一個普通對象/函數(shù)
????????????????resolve(x);
????????????}
????????}?catch(e)?{
????????????if(called)?return;
????????????called?=?true;
????????????reject(e);
????????}
????}?else?{
????????resolve(x);
????}
}
/**
?*?[注冊fulfilled狀態(tài)/rejected狀態(tài)對應(yīng)的回調(diào)函數(shù)]
?*?@param??{function}?onFulfilled?fulfilled狀態(tài)時?執(zhí)行的函數(shù)
?*?@param??{function}?onRejected??rejected狀態(tài)時?執(zhí)行的函數(shù)
?*?@return?{function}?newPromsie??返回一個新的promise對象
?*/
Promise.prototype.then?=?function(onFulfilled,?onRejected)?{
????const?that?=?this;
????let?newPromise;
????//?處理參數(shù)默認值?保證參數(shù)后續(xù)能夠繼續(xù)執(zhí)行
????onFulfilled?=
????????typeof?onFulfilled?===?"function"???onFulfilled?:?value?=>?value;
????onRejected?=
????????typeof?onRejected?===?"function"???onRejected?:?reason?=>?{
????????????throw?reason;
????????};
????//?then里面的FULFILLED/REJECTED狀態(tài)時?為什么要加setTimeout??
????//?原因:
????//?其一?2.2.4規(guī)范?要確保?onFulfilled?和?onRejected?方法異步執(zhí)行(且應(yīng)該在?then?方法被調(diào)用的那一輪事件循環(huán)之后的新執(zhí)行棧中執(zhí)行)?所以要在resolve里加上setTimeout
????//?其二?2.2.6規(guī)范?對于一個promise,它的then方法可以調(diào)用多次.(當在其他程序中多次調(diào)用同一個promise的then時?由于之前狀態(tài)已經(jīng)為FULFILLED/REJECTED狀態(tài),則會走的下面邏輯),所以要確保為FULFILLED/REJECTED狀態(tài)后?也要異步執(zhí)行onFulfilled/onRejected
????//?其二?2.2.6規(guī)范?也是resolve函數(shù)里加setTimeout的原因
????//?總之都是?讓then方法異步執(zhí)行?也就是確保onFulfilled/onRejected異步執(zhí)行
????//?如下面這種情景?多次調(diào)用p1.then
????//?p1.then((value)?=>?{?//?此時p1.status?由pedding狀態(tài)?=>?fulfilled狀態(tài)
????//?????console.log(value);?//?resolve
????//?????//?console.log(p1.status);?//?fulfilled
????//?????p1.then(value?=>?{?//?再次p1.then?這時已經(jīng)為fulfilled狀態(tài)?走的是fulfilled狀態(tài)判斷里的邏輯?所以我們也要確保判斷里面onFuilled異步執(zhí)行
????//?????????console.log(value);?//?'resolve'
????//?????});
????//?????console.log('當前執(zhí)行棧中同步代碼');
????//?})
????//?console.log('全局執(zhí)行棧中同步代碼');
????//
????if?(that.status?===?FULFILLED)?{?//?成功態(tài)
????????return?newPromise?=?new?Promise((resolve,?reject)?=>?{
????????????setTimeout(()?=>?{
????????????????try{
????????????????????let?x?=?onFulfilled(that.value);
????????????????????resolvePromise(newPromise,?x,?resolve,?reject);?//?新的promise?resolve?上一個onFulfilled的返回值
????????????????}?catch(e)?{
????????????????????reject(e);?//?捕獲前面onFulfilled中拋出的異常?then(onFulfilled,?onRejected);
????????????????}
????????????});
????????})
????}
????if?(that.status?===?REJECTED)?{?//?失敗態(tài)
????????return?newPromise?=?new?Promise((resolve,?reject)?=>?{
????????????setTimeout(()?=>?{
????????????????try?{
????????????????????let?x?=?onRejected(that.reason);
????????????????????resolvePromise(newPromise,?x,?resolve,?reject);
????????????????}?catch(e)?{
????????????????????reject(e);
????????????????}
????????????});
????????});
????}
????if?(that.status?===?PENDING)?{?//?等待態(tài)
????????//?當異步調(diào)用resolve/rejected時?將onFulfilled/onRejected收集暫存到集合中
????????return?newPromise?=?new?Promise((resolve,?reject)?=>?{
????????????that.onFulfilledCallbacks.push((value)?=>?{
????????????????try?{
????????????????????let?x?=?onFulfilled(value);
????????????????????resolvePromise(newPromise,?x,?resolve,?reject);
????????????????}?catch(e)?{
????????????????????reject(e);
????????????????}
????????????});
????????????that.onRejectedCallbacks.push((reason)?=>?{
????????????????try?{
????????????????????let?x?=?onRejected(reason);
????????????????????resolvePromise(newPromise,?x,?resolve,?reject);
????????????????}?catch(e)?{
????????????????????reject(e);
????????????????}
????????????});
????????});
????}
};
/**
?*?Promise.all?Promise進行并行處理
?*?參數(shù):?promise對象組成的數(shù)組作為參數(shù)
?*?返回值:?返回一個Promise實例
?*?當這個數(shù)組里的所有promise對象全部變?yōu)閞esolve狀態(tài)的時候,才會resolve。
?*/
Promise.all?=?function(promises)?{
????return?new?Promise((resolve,?reject)?=>?{
????????let?done?=?gen(promises.length,?resolve);
????????promises.forEach((promise,?index)?=>?{
????????????promise.then((value)?=>?{
????????????????done(index,?value)
????????????},?reject)
????????})
????})
}
function?gen(length,?resolve)?{
????let?count?=?0;
????let?values?=?[];
????return?function(i,?value)?{
????????values[i]?=?value;
????????if?(++count?===?length)?{
????????????console.log(values);
????????????resolve(values);
????????}
????}
}
/**
?*?Promise.race
?*?參數(shù):?接收?promise對象組成的數(shù)組作為參數(shù)
?*?返回值:?返回一個Promise實例
?*?只要有一個promise對象進入?FulFilled?或者?Rejected?狀態(tài)的話,就會繼續(xù)進行后面的處理(取決于哪一個更快)
?*/
Promise.race?=?function(promises)?{
????return?new?Promise((resolve,?reject)?=>?{
????????promises.forEach((promise,?index)?=>?{
???????????promise.then(resolve,?reject);
????????});
????});
}
//?用于promise方法鏈時?捕獲前面onFulfilled/onRejected拋出的異常
Promise.prototype.catch?=?function(onRejected)?{
????return?this.then(null,?onRejected);
}
Promise.resolve?=?function?(value)?{
????return?new?Promise(resolve?=>?{
????????resolve(value);
????});
}
Promise.reject?=?function?(reason)?{
????return?new?Promise((resolve,?reject)?=>?{
????????reject(reason);
????});
}
/**
?*?基于Promise實現(xiàn)Deferred的
?*?Deferred和Promise的關(guān)系
?*?-?Deferred?擁有?Promise
?*?-?Deferred?具備對?Promise的狀態(tài)進行操作的特權(quán)方法(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;
}
/**
?*?Promise/A+規(guī)范測試
?*?npm?i?-g?promises-aplus-tests
?*?promises-aplus-tests?Promise.js
?*/
try?{
??module.exports?=?Promise
}?catch?(e)?{
}
Promise測試
npm?i?-g?promises-aplus-testspromises-aplus-tests?Promise.js
如何主動終止Promise調(diào)用鏈
const?p1?=?new?Promise((resolve,?reject)?=>?{??setTimeout(()?=>?{?//?異步操作
??????resolve('start')
??},?1000);
});
p1.then((result)?=>?{
???console.log('a',?result);?
???return?Promise.reject('中斷后續(xù)調(diào)用');?//?此時rejected的狀態(tài)將直接跳到catch里,剩下的調(diào)用不會再繼續(xù)
}).then(result?=>?{
???console.log('b',?result);
}).then(result?=>?{
???console.log('c',?result);
}).catch(err?=>?{
???console.log(err);
});
//?a?start
//?中斷后續(xù)調(diào)用
珠峰前端課程開課時間
2019年3月11日?《零基礎(chǔ)入門課程》?2019年3月4日? ?《全日制框架課程》
2019年3月4日???《周末班框架課程》?
2019年3月11日?《前端就業(yè)課程》
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的promise then err_Promise 原理解析与实现(遵循Promise/A+规范)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编程实现算术表达式求值_用魔法打败魔法:
- 下一篇: android udp 收发例子_网络协