原文地址:http://blog.getify.com/promis...javascript
如今,我但願你已經看過深刻理解Promise的前三篇文章了。而且假設你已經徹底理解Promises是什麼以及深刻討論Promises的重要性。java
回到2005年,Prototype.js框架是最早提出擴展Javascript原生對象的內置prototype屬性的框架之一。它們的想法是咱們能夠經過向prototype屬性添加額外的方法來擴展示有的功能。git
若是你對近十年Javascript編程作一個簡單的調查,好比使用google簡單搜索下,你會發現對於這個想法有不少反對意見。它們都是有緣由的。github
大多數開發者會告訴你:「不要擴展原生對象」或者「只在polyfill的時候擴展原生對象」。後者意味着只有當擴展的功能已經被列入規範而後你只是爲了能在舊的環境中使用這些功能的時候才能對元素對象進行擴展。ajax
想象一個真實的場景(確實發生在我身上):回到Netscape3/4和IE4的時代,當時的JS並無如今這麼友好。做爲許多顯著差別中的一個,數組並無push(..)
方法來向它的尾部添加元素。編程
因此,一些人會經過下面這段代碼來擴展:segmentfault
//Netscape 4 doesn't hava Array.push Array.prototype.push = function(elem){ this[this.length - 1] = elem ; }
乍一看可能以爲沒問題,可是你很快就會發現一些問題。數組
這裏須要一個if
來判斷原生是否有對於push(..)
的支持,若是有咱們就可使用原生的push(..)
方法。promise
咱們須要注意咱們已經破壞了數組對象for..in
循環行爲,由於咱們的push(..)
方法會出如今for..in
循環的結果中。固然,你不該該在數組上使用for..in
循環,這又是另一個話題了。安全
有一個和1相關的更大的問題。不只僅是須要一個if
判斷:
if(!Array.prototype.push){ //make our own }
咱們應該問問咱們本身,若是內置的push(..)
實現和咱們的實現不兼容怎麼辦?若是內置的實現接受不同數量的參數或者不同的參數類型怎麼辦?
若是咱們的代碼依賴於咱們本身實現的push(..)
,而後咱們只是簡單的用新的方法替換咱們本身的方法,那麼代碼會出現問題。若是咱們的實現覆蓋了內置的push(..)
實現,而後若是一些JS庫指望使用內置的標準push(..)
方法怎麼辦?
這些問題是真實發生在我身上的。我有一個工做是在一個用戶的古老的網站上加入一個組件,而後這個組件依賴於jQuery。咱們組件在其餘網站均可以正常使用,可是在這個特殊的站點卻沒法使用。我花了不少時間來找出問題。
最終,我定位到了上面那個if
代碼片斷。這裏有什麼問題呢?
它的push(..)
實現只接受一個參數,然而jQuery中指望是經過push(el1,el2,...)
來調用push方法,因此它就沒法正常運行了。
Oops。
可是猜猜當我移除原來的push代碼時發生了什麼?在其餘網站這個組件也不能使用的。爲何?我仍是不知道具體是爲何。我認爲他們意外地依賴於外部變量,而這些外部變量沒有傳遞進來。
可是,真正的問題是,有人經過一種對於將來存在潛在危險的方式擴展內置原生對象,致使這個方法在將來可能沒法正常運行。
我不是惟一遇到這個問題的人。成千上萬的開發者都遇到了這種狀況。咱們中的大多數認爲你必須十分當心當你擴展原生JS對象時。若是你這麼作了,你最好不要使用跟語言新版本中的方法名相同的名字。
爲何全部的老爺爺抱怨現在Promises的火熱呢?由於那些開發Promise
"polyfills"的人彷佛忘記或者拋棄了老人們的智慧。他們開始直接往Promise
和Promsie.prototype
上加額外的東西。
我真的須要再去解釋爲何這是一個「將來的」壞點子嗎?
咱們能夠一直爭論這個問題到死,可是仍然不能改變這個事實。若是你擴展原生對象,你就是和將來敵對的,就算你以爲你本身已經作得很好了。
並且,你用越大衆化的名字來擴展原生對象,你越有可能影響將來的人。讓咱們看看Bluebird庫,由於它是最流行的Promise
polyfill/庫之一。它足夠快可是它跟其餘庫比起來也更加大。
可是速度和大小並非我如今擔憂的。我關心的是它選擇了把本身添加到Promise
的命名空間上。就算它使用一個polyfill安全的方式,實際上並無,事實就算它添加許多額外的東西到原生對象上。
例如,Bluebird添加了Promise.method(..)
:
function someAsyncNonPromiseFunc() { // ... } var wrappedFn = Promise.method( someAsyncNonPromiseFunc ); var p = wrappedFn(..); p.then(..)..;
看起來沒什麼問題,是嗎?固然。可是若是某天規範須要添加Promise.method(..)
方法。而後若是它的行爲和Bluebird有很大的區別會怎麼樣呢?
你又會看到Array.prototype.push(..)
同樣的狀況。
Bluebird添加了許多東西到原生的Promise
。因此有不少可能性會在將來會發生衝突。我但願我歷來不須要去修復某我的的Promise擴展代碼。可是,我極可能須要這麼作。
可是這還不是最糟的。若是Bluebir很是流行,而後許多現實中的網站依賴於這麼一個擴展,忽然一天TC39協會經過某種方式強制避免擴展官方規範,那麼這些依賴於擴展的網站都將崩潰。
你看,這就是擴展原生對象的危險所在:你爲了實現你的功能而後擴展原生對象,而後就拍拍屁股把這些爛攤子留給了TC39成員們。由於你愚蠢的決定Javascript的維護者只能選擇其餘機制。
不相信我?這種狀況已經發生不少次了。你知道爲何在19年的JS歷史中typeof null === "object"
這個bug一直沒法修復嗎?由於太多的代碼都依賴於這段代碼了。若是他們修復了這個bug,那麼結果可想而知。
我真的不想這種事情發生在Promsie
身上。請中止經過擴展原生對象來定義Promise polyfill/庫。
我認爲咱們須要更多不破壞規範的polyfill,像個人"Native Promise Only"。咱們須要良好,穩固,性能優秀可是和標準兼容的polyfill。
特別的,咱們須要它們以便於那些須要擴展promise的人能夠在這個包裝上進行操做。咱們不該該很容易得到一個Promise
polyfill而後建立咱們本身的SuperAwesomePromise
包裝在它上面嗎?
已經有不少的好例子了,好比Q和when,我本身也寫了一個,叫作asnquence(async + sequence),個人是設計來隱藏promises的,由於promise是低級別的API,因此與其給你一個簡單的抽象的東西不如隱藏醜陋的細節。
例如,比較下下面兩段代碼。
原生Promises:
function delay(n) { return new Promise( function(resolve,reject){ setTimeout( resolve, n ); } ); } function request(url) { return new Promise( function(resolve,reject){ ajax( url, function(err,res){ if (err) reject( err ); else resolve( res ); } ); } ); } delay( 100 ) .then( function(){ return request( "some/url" ); } ) .then( success, error );
asynquence:
function delay(n) { return ASQ( function(done){ setTimeout( done, n ); } ); } function request(url) { return ASQ( function(done){ ajax( url, done.errfcb ); } ); } delay( 100 ) .val( "some/url" ) .seq( request ) .then( success ) .or( error );
但願你可以經過這個簡單的例子看出asynquence是如何下降使用promises來表達異步流程的難度的。它在底層實現爲你建立promise,它自動把它們鏈接在一塊兒,而後爲一樣的組合模式提供了簡單的語法。
顯然,我認爲asynquence是很是使人驚奇的。我認爲你應該看看一些例子,而後看看你們擴展的插件,這些插件使得它能提供更多的便利。
若是asynquence不是你的菜,那麼你能夠再尋找一個適合你的好用知名的抽象庫。
可是請不要使用那些擴展原生Promise
的庫。這對於將來不是一件好事。
Promise是使人驚奇的而且它們正在改變許多JS開發者編寫和維護一部流程的方式。ES6帶來的原生Promise
是這個語言一個重大的勝利。爲了加速這個勝利的過程,咱們中的許多人開發Promise polyfill和Promise庫。
可是不要由於Promise帶來的興奮和喜悅讓你忘了一個不能否認的事實:擴展原生對象是一件危險而且充滿冒險的事情,並僅僅對於庫的做者也包括使用這些庫的全部人。
最後,請負有責任感而且使用安全的promise擴展。咱們在未來會感謝你的。
深刻理解Promise五部曲--1.異步問題
深刻理解Promise五部曲--2.轉換問題
深刻理解Promise五部曲--3.可靠性問題
深刻理解Promise五部曲--4.擴展性問題
深刻理解Promise五部曲--5.樂高問題
最後,安利下個人我的博客,歡迎訪問:http://bin-playground.top