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