摘要:有一個和相關(guān)的更大的問題。最后,請負(fù)有責(zé)任感并且使用安全的擴(kuò)展。深入理解五部曲異步問題深入理解五部曲轉(zhuǎn)換問題深入理解五部曲可靠性問題深入理解五部曲擴(kuò)展性問題深入理解五部曲樂高問題最后,安利下我的個人博客,歡迎訪問
原文地址:http://blog.getify.com/promis...
現(xiàn)在,我希望你已經(jīng)看過深入理解Promise的前三篇文章了。并且假設(shè)你已經(jīng)完全理解Promises是什么以及深入討論Promises的重要性。
不要擴(kuò)展原生對象!回到2005年,Prototype.js框架是最先提出擴(kuò)展Javascript原生對象的內(nèi)置prototype屬性的框架之一。它們的想法是我們可以通過向prototype屬性添加額外的方法來擴(kuò)展現(xiàn)有的功能。
如果你對近十年Javascript編程做一個簡單的調(diào)查,比如使用google簡單搜索下,你會發(fā)現(xiàn)對于這個想法有很多反對意見。它們都是有原因的。
大多數(shù)開發(fā)者會告訴你:“不要擴(kuò)展原生對象”或者“只在polyfill的時候擴(kuò)展原生對象”。后者意味著只有當(dāng)擴(kuò)展的功能已經(jīng)被列入規(guī)范然后你只是為了能在舊的環(huán)境中使用這些功能的時候才能對元素對象進(jìn)行擴(kuò)展。
數(shù)組Push方法想象一個真實的場景(確實發(fā)生在我身上):回到Netscape3/4和IE4的時代,當(dāng)時的JS并沒有現(xiàn)在這么友好。作為許多顯著差異中的一個,數(shù)組并沒有push(..)方法來向它的尾部添加元素。
所以,一些人會通過下面這段代碼來擴(kuò)展:
//Netscape 4 doesn"t hava Array.push Array.prototype.push = function(elem){ this[this.length - 1] = elem ; }
乍一看可能覺得沒問題,但是你很快就會發(fā)現(xiàn)一些問題。
這里需要一個if來判斷原生是否有對于push(..)的支持,如果有我們就可以使用原生的push(..)方法。
我們需要注意我們已經(jīng)破壞了數(shù)組對象for..in循環(huán)行為,因為我們的push(..)方法會出現(xiàn)在for..in循環(huán)的結(jié)果中。當(dāng)然,你不應(yīng)該在數(shù)組上使用for..in循環(huán),這又是另外一個話題了。
有一個和1相關(guān)的更大的問題。不僅僅是需要一個if判斷:
if(!Array.prototype.push){ //make our own }
我們應(yīng)該問問我們自己,如果內(nèi)置的push(..)實現(xiàn)和我們的實現(xiàn)不兼容怎么辦?如果內(nèi)置的實現(xiàn)接受不一樣數(shù)量的參數(shù)或者不一樣的參數(shù)類型怎么辦?
如果我們的代碼依賴于我們自己實現(xiàn)的push(..),然后我們只是簡單的用新的方法替換我們自己的方法,那么代碼會出現(xiàn)問題。如果我們的實現(xiàn)覆蓋了內(nèi)置的push(..)實現(xiàn),然后如果一些JS庫期望使用內(nèi)置的標(biāo)準(zhǔn)push(..)方法怎么辦?
這些問題是真實發(fā)生在我身上的。我有一個工作是在一個用戶的古老的網(wǎng)站上加入一個組件,然后這個組件依賴于jQuery。我們組件在其他網(wǎng)站都可以正常使用,但是在這個特殊的站點卻無法使用。我花了很多時間來找出問題。
最終,我定位到了上面那個if代碼片段。這里有什么問題呢?
它的push(..)實現(xiàn)只接受一個參數(shù),然而jQuery中期望是通過push(el1,el2,...)來調(diào)用push方法,所以它就無法正常運行了。
Oops。
但是猜猜當(dāng)我移除原來的push代碼時發(fā)生了什么?在其他網(wǎng)站這個組件也不能使用的。為什么?我還是不知道具體是為什么。我認(rèn)為他們意外地依賴于外部變量,而這些外部變量沒有傳遞進(jìn)來。
但是,真正的問題是,有人通過一種對于未來存在潛在危險的方式擴(kuò)展內(nèi)置原生對象,導(dǎo)致這個方法在未來可能無法正常運行。
我不是唯一遇到這個問題的人。成千上萬的開發(fā)者都遇到了這種情況。我們中的大多數(shù)認(rèn)為你必須十分小心當(dāng)你擴(kuò)展原生JS對象時。如果你這么做了,你最好不要使用跟語言新版本中的方法名相同的名字。
Promise擴(kuò)展為什么所有的老爺爺抱怨如今Promises的火熱呢?因為那些開發(fā)Promise"polyfills"的人似乎忘記或者拋棄了老人們的智慧。他們開始直接往Promise和Promsie.prototype上加額外的東西。
我真的需要再去解釋為什么這是一個“未來的”壞點子嗎?
Blue In The Face我們可以一直爭論這個問題到死,但是仍然不能改變這個事實。如果你擴(kuò)展原生對象,你就是和未來敵對的,就算你覺得你自己已經(jīng)做得很好了。
而且,你用越大眾化的名字來擴(kuò)展原生對象,你越有可能影響未來的人。讓我們看看Bluebird庫,因為它是最流行的Promisepolyfill/庫之一。它足夠快但是它跟其他庫比起來也更加大。
但是速度和大小并不是我現(xiàn)在擔(dān)心的。我關(guān)心的是它選擇了把自己添加到Promise的命名空間上。就算它使用一個polyfill安全的方式,實際上并沒有,事實就算它添加許多額外的東西到原生對象上。
例如,Bluebird添加了Promise.method(..):
function someAsyncNonPromiseFunc() { // ... } var wrappedFn = Promise.method( someAsyncNonPromiseFunc ); var p = wrappedFn(..); p.then(..)..;
看起來沒什么問題,是嗎?當(dāng)然。但是如果某天規(guī)范需要添加Promise.method(..)方法。然后如果它的行為和Bluebird有很大的區(qū)別會怎么樣呢?
你又會看到Array.prototype.push(..)一樣的情況。
Bluebird添加了許多東西到原生的Promise。所以有很多可能性會在未來會發(fā)生沖突。我希望我從來不需要去修復(fù)某個人的Promise擴(kuò)展代碼。但是,我很可能需要這么做。
未來約束但是這還不是最糟的。如果Bluebir非常流行,然后許多現(xiàn)實中的網(wǎng)站依賴于這么一個擴(kuò)展,突然一天TC39協(xié)會通過某種方式強(qiáng)制避免擴(kuò)展官方規(guī)范,那么這些依賴于擴(kuò)展的網(wǎng)站都將崩潰。
你看,這就是擴(kuò)展原生對象的危險所在:你為了實現(xiàn)你的功能然后擴(kuò)展原生對象,然后就拍拍屁股把這些爛攤子留給了TC39成員們。因為你愚蠢的決定Javascript的維護(hù)者只能選擇其他機(jī)制。
不相信我?這種情況已經(jīng)發(fā)生很多次了。你知道為什么在19年的JS歷史中typeof null === "object"這個bug一直無法修復(fù)嗎?因為太多的代碼都依賴于這段代碼了。如果他們修復(fù)了這個bug,那么結(jié)果可想而知。
我真的不想這種事情發(fā)生在Promsie身上。請停止通過擴(kuò)展原生對象來定義Promise polyfill/庫。
我認(rèn)為我們需要更多不破壞規(guī)范的polyfill,像我的"Native Promise Only"。我們需要良好,穩(wěn)固,性能優(yōu)秀但是和標(biāo)準(zhǔn)兼容的polyfill。
特別的,我們需要它們以便于那些需要擴(kuò)展promise的人可以在這個包裝上進(jìn)行操作。我們不應(yīng)該很容易獲得一個Promisepolyfill然后創(chuàng)建我們自己的SuperAwesomePromise包裝在它上面嗎?
已經(jīng)有很多的好例子了,比如Q和when,我自己也寫了一個,叫做asnquence(async + sequence),我的是設(shè)計來隱藏promises的,因為promise是低級別的API,所以與其給你一個簡單的抽象的東西不如隱藏丑陋的細(xì)節(jié)。
例如,比較下下面兩段代碼。
原生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來表達(dá)異步流程的難度的。它在底層實現(xiàn)為你創(chuàng)建promise,它自動把它們連接在一起,然后為同樣的組合模式提供了簡單的語法。
顯然,我認(rèn)為asynquence是非常令人驚奇的。我認(rèn)為你應(yīng)該看看一些例子,然后看看大家擴(kuò)展的插件,這些插件使得它能提供更多的便利。
如果asynquence不是你的菜,那么你可以再尋找一個適合你的好用知名的抽象庫。
但是請不要使用那些擴(kuò)展原生Promise的庫。這對于未來不是一件好事。
Promise是令人驚奇的并且它們正在改變許多JS開發(fā)者編寫和維護(hù)一部流程的方式。ES6帶來的原生Promise是這個語言一個重大的勝利。為了加速這個勝利的過程,我們中的許多人開發(fā)Promise polyfill和Promise庫。
但是不要因為Promise帶來的興奮和喜悅讓你忘了一個不可否認(rèn)的事實:擴(kuò)展原生對象是一件危險并且充滿冒險的事情,并僅僅對于庫的作者也包括使用這些庫的所有人。
最后,請負(fù)有責(zé)任感并且使用安全的promise擴(kuò)展。我們在將來會感謝你的。
深入理解Promise五部曲--1.異步問題
深入理解Promise五部曲--2.轉(zhuǎn)換問題
深入理解Promise五部曲--3.可靠性問題
深入理解Promise五部曲--4.擴(kuò)展性問題
深入理解Promise五部曲--5.樂高問題
最后,安利下我的個人博客,歡迎訪問:http://bin-playground.top
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/87587.html
摘要:當(dāng)引擎開始執(zhí)行一個函數(shù)比如回調(diào)函數(shù)時,它就會把這個函數(shù)執(zhí)行完,也就是說只有執(zhí)行完這段代碼才會繼續(xù)執(zhí)行后面的代碼。當(dāng)條件允許時,回調(diào)函數(shù)就會被運行。現(xiàn)在,返回去執(zhí)行注冊的那個回調(diào)函數(shù)。 原文地址:http://blog.getify.com/promis... 在微博上看到有人分享LabJS作者寫的關(guān)于Promise的博客,看了下覺得寫得很好,分五個部分講解了Promise的來龍去脈。從...
摘要:直到最近,我們?nèi)匀辉谟煤唵蔚幕卣{(diào)函數(shù)來處理異步的問題。當(dāng)我們只有一個異步任務(wù)的時候使用回調(diào)函數(shù)看起來還不會有什么問題。 原文地址:http://blog.getify.com/promis... 廈門旅行歸來,繼續(xù)理解Promise 在上一篇深入理解Promise五部曲:1.異步問題中,我們揭示了JS的異步事件輪詢并發(fā)模型并且解釋了多任務(wù)是如何相互穿插使得它們看起來像是同時運行的。...
摘要:簡單的說,即將到來的標(biāo)準(zhǔn)指出是一個,所以作為一個,必須可以被子類化。保護(hù)還是子類化這是個問題我真的希望我能創(chuàng)建一個忠實的給及以下。 原文地址:http://blog.getify.com/promis... 如果你需要趕上我們關(guān)于Promise的進(jìn)度,可以看看這個系列前兩篇文章深入理解Promise五部曲--1.異步問題和深入理解Promise五部曲--2.控制權(quán)轉(zhuǎn)移問題。 Promi...
摘要:一個就像一個樂高玩具。問題是不是你小時候玩兒的那個有趣,它們不是充滿想象力的打氣筒,也不是一種樂高玩具。這是對的并不是給開發(fā)者使用的,它們是給庫作者使用的。不會超過這兩種情況。第二個是根據(jù)第一個處理函數(shù)如何運行來自動變成狀態(tài)成功或者失敗。 原文地址:http://blog.getify.com/promis... 在 Part4:擴(kuò)展問題 中,我討論了如何擴(kuò)展和抽象Promise是多么...
摘要:只要在調(diào)用異步函數(shù)時設(shè)置一個或多個回調(diào)函數(shù),函數(shù)就會在完成時自動調(diào)用回調(diào)函數(shù)。要解決的問題是,如何將回調(diào)方法的參數(shù)從回調(diào)方法中傳遞出來,讓它可以像同步函數(shù)的返回結(jié)果一樣,在回調(diào)函數(shù)以外的控制范圍內(nèi),可以傳遞和復(fù)用。 摘要: 我們知道 JavaScript 自從有了 Generator 之后,就有了各種基于 Generator 封裝的協(xié)程。其中 hprose 中封裝的 Promise 和...
閱讀 1428·2021-11-22 15:24
閱讀 2519·2021-10-11 11:06
閱讀 2323·2021-10-09 09:45
閱讀 2525·2021-09-09 09:33
閱讀 634·2019-08-30 15:53
閱讀 1439·2019-08-30 12:48
閱讀 656·2019-08-29 13:47
閱讀 500·2019-08-26 18:27