摘要:這下不管是同步還是異步,我們隨時(shí)可以在方法中去取值,如果值沒有被,也就是說(shuō)狀態(tài)沒發(fā)生變化,將給我們記錄下這件事,等到的那個(gè)時(shí)間點(diǎn)把值傳給方法中那個(gè)回調(diào)函數(shù),。
一本正經(jīng)的扯淡
顧名思義,promise中文意思就是承諾,也就是現(xiàn)在實(shí)現(xiàn)不了將來(lái)?????,但是將來(lái)這玩意誰(shuí)說(shuō)的準(zhǔn)呢。就像你去泡妞,你可能許下各種諾言,但能不能實(shí)現(xiàn),完全取決于你這人靠不靠譜。好在計(jì)算機(jī)不是人,不是人,不是人,????正因?yàn)椴皇侨耍运S下的承諾,它就一定會(huì)給你一個(gè)結(jié)果。
等待承諾實(shí)現(xiàn)的過程中很漫長(zhǎng),所以你可以做一些其它的事情,沒必要老是堵在這一條道上,也就是異步。打個(gè)比方,你打電話給飯店老板叫了個(gè)外賣,老板告訴你,10分鐘后送過去,也就是說(shuō)老板給了你一個(gè)承諾,于是你等啊等,這中間又去上了個(gè)廁所,玩了會(huì)手機(jī)??????,這就是異步,老板給的承諾并沒有妨礙你干其它的事情。OK,扯淡結(jié)束。
為了實(shí)現(xiàn)異步,一般要設(shè)置一個(gè)回調(diào)函數(shù)
setTimeout(function(){ console.log(1); setTimeout(function(){ console.log(2); setTimeout(function(){ console.log(3); setTimeout(function(){ console.log(4); setTimeout(function(){ console.log(5); },500) },400) },300) },200) },100);
??????有沒有一種想死的感覺!
promise最大優(yōu)勢(shì)就是第一消滅了這種回調(diào)地獄,第二增加了錯(cuò)誤捕獲,像下面這種,
promis.then(function (response) { //do something; }, function (reason) { //get error }).then(function (response) { //do something; }, function (reason) { //get error }).then(function (response) { //do something; }, function (reason) { //get error });
如果不做錯(cuò)誤處理則更清晰
promis.then(function (response) { //do something; }).then(function (response) { //do something; }).then(function (response) { //do something; }).then(function (response) { //do something; });
它使得異步的代碼看起來(lái)像是在同步執(zhí)行,大大增強(qiáng)了代碼的可讀性。
美中不足的是你得寫一堆的.then(function(){},function(){}),但是和回調(diào)地獄相比,忍了。
在ES7中會(huì)有號(hào)稱是異步的終極解決方案,async和await,那是后話。
前面說(shuō)了,計(jì)算機(jī)不是人,所以它許下的承諾,它一定會(huì)給你一個(gè)結(jié)果,不管這個(gè)承諾的結(jié)果是接受還是拒絕。
所以,第一,promise一定會(huì)返回一個(gè)結(jié)果。
第二,這個(gè)結(jié)果是不可逆的,你只能接受,本質(zhì)是因?yàn)閜romise的狀態(tài)不可逆,一旦它變成了resolve或者reject,你休想再讓你變成pending,否則,它要會(huì)說(shuō)話,肯定回你的只有一個(gè)字,滾!
第三、promise的結(jié)果什么時(shí)候返回,你說(shuō)了不算,你去泡妞的時(shí)候,妞也不一定當(dāng)場(chǎng)就答應(yīng)你吧,或許想個(gè)三、五天也說(shuō)不定,這個(gè)主動(dòng)權(quán)不是掌握在你手中的。
第四、ES6的promise執(zhí)行過程中,你是無(wú)法獲得執(zhí)行的進(jìn)度的,到底它現(xiàn)在是pending還是resolve,還是reject。就好像妞和她的閨蜜探討要不要接受你,你是打聽不到的。當(dāng)然并不是完全不能,例如angularjs的$q實(shí)現(xiàn)一個(gè)notify方法,可以獲取到執(zhí)行進(jìn)度的通知。
最后說(shuō)一點(diǎn)兒你的權(quán)力,你能決定的是在什么時(shí)候去取promise的結(jié)果,也就是調(diào)用then方法的時(shí)間,就好像你每天追著妞問,你想好沒有??????,妞這個(gè)時(shí)候會(huì)有三種回答,一是答應(yīng)你,二是拒絕你,三是還得再想想,XXXX時(shí)候再告訴你????,也就說(shuō)這TMD又是一個(gè)承諾?????。
咳、咳,現(xiàn)在開始必須嚴(yán)肅點(diǎn),畢竟技術(shù)是一件嚴(yán)肅的事情。
說(shuō)白了,promise就是一個(gè)對(duì)象,一個(gè)只能通過then方法來(lái)取得它上面的值的對(duì)象。
在es6中,你只要大喊一句,妞,給我個(gè)承諾,它就會(huì)給你一個(gè)promise,就像下面這樣:
var promise = new Promise(function(resolve,reject){ //do something; })
然后你就可以調(diào)用它的then方法去取值,那么從這個(gè)角度看,這個(gè)構(gòu)造函數(shù)一定是返回了一個(gè)帶有then方法的對(duì)象。另外還有狀態(tài),狀態(tài)的變化不可逆。再加上些其它的方法,如all、catch???,不過不要著急,我們一步一步來(lái)意淫出這個(gè)漂亮的妞????
1、通常情況,我們使用回調(diào)一個(gè)函數(shù)內(nèi)執(zhí)行另外一個(gè)函數(shù):
function doSomething(callback){ console.log("do something"); callback(); } doSomething(function(){ console.log("a"); });
2、但是在使用promise時(shí),我們是用then方法去取結(jié)果,而promise就是個(gè)對(duì)象,那么上面的代碼看起來(lái)應(yīng)該這樣寫:
function doSomething(){ console.log("a"); return { then: function(callback){ var value = 1; callback(value); } } } doSomething().then(function(res){ console.log(res); });
在這里,我們調(diào)用dosomething函數(shù)時(shí),返回了一個(gè)帶有then方法的對(duì)象,然后在then方法回調(diào)中去執(zhí)行,現(xiàn)在看來(lái)是不是有那么點(diǎn)樣子了,時(shí)刻記得兩件事,對(duì)象, then方法。
3、在ES6中Promise是一個(gè)構(gòu)造函數(shù),這簡(jiǎn)單,給這個(gè)dosomething換個(gè)名字,
function Promise(){ this.then = function(callback){ var value = 1; callback(value); } }
在實(shí)例化promise的時(shí)候,要傳一個(gè)函數(shù)進(jìn)去,這也簡(jiǎn)單
function Promise(fn){ this.then = function(callback){ callback(); } }
實(shí)例化傳入的函數(shù)fn中(下文中的fn都是指代這個(gè)匿名函數(shù)),你會(huì)傳入2個(gè)參數(shù),一個(gè)叫resolve,另一個(gè)叫reject,為了簡(jiǎn)單起見,我們不考慮reject,它的道理和resolve是一樣的。那么就像這樣:
var promise = new Promise(function(resolve){ var value = 1; resolve(value); })
即然傳了一個(gè)fn函數(shù)進(jìn)去,那么在實(shí)例化過程中,這個(gè)函數(shù)一定會(huì)在某個(gè)時(shí)刻執(zhí)行。執(zhí)行時(shí),它又會(huì)接收到一個(gè)參數(shù)resolve,這個(gè)resolve一定是一個(gè)函數(shù),這點(diǎn)從上面就可以很明顯的看出來(lái),resolve在實(shí)例化時(shí)執(zhí)行了,而且接收到了一個(gè)參數(shù),在這里是變量value。那么Promise函數(shù)內(nèi)部很可能是這樣:
function Promise(fn){ function resolve(value){} this.then = function (onResolved) {}; fn(resolve); }
為了看起來(lái)更直接,這里我們把調(diào)用then方法傳的第一個(gè)函數(shù)就叫做onResolved,那么接下來(lái)我們應(yīng)該考慮在實(shí)例化的時(shí)候,還有什么事情要做,在then方法的回調(diào)函數(shù)中我們希望得到promise的值,這個(gè)值是在fn函數(shù)調(diào)用后被resolve函數(shù)運(yùn)算后得到的,最終要在onResolved函數(shù)中拿到,也就是說(shuō),我們必須在resolve中將這個(gè)值傳遞給onResolved,迂回一下:
function Promise(fn) { var callback = null; function resolve(value) { callback(value); } this.then = function(onResolved) { callback = onResolved; }; fn(resolve); }
但是這里有一個(gè)問題,就是我們調(diào)用resolve方法時(shí),還沒有調(diào)用過then方法,因此callbak是null,瀏覽器報(bào)錯(cuò):callback is not a function,這里hack下,讓resolve方法的執(zhí)行在then之后。
function Promise(fn) { var callback = null; function resolve(value) { setTimeout(function(){ callback(value); },0) } this.then = function(onResolved) { callback = onResolved; }; fn(resolve); }
執(zhí)行一下,
var promise = new Promise(function(res){ var value = 2; res(2); }); promise.then(function(res){ console.log(res); })
OK,成功的輸出。目前為止,promise的輪廓算是被我們意淫出來(lái)了。
4、promise是有狀態(tài)的,而且狀態(tài)不可逆,同樣的為了簡(jiǎn)單起見,我先來(lái)搞定從pending變到resolved,那么rejected也一樣。仔細(xì)想下,執(zhí)行了resolve方法后可以得到一個(gè)resolved狀態(tài)的值,那么必然在resolve方法中會(huì)去改變promise的狀態(tài),并且得到這個(gè)值,那么代碼貌似應(yīng)該這樣寫:
function Promise(fn) { var state = "pending"; function resolve(newValue) { state = "resolved"; callback(newValue); } this.then = function(onResolved) { callback = onResolved; }; fn(resolve); }
這里我們先把setTimeout這家伙給干掉了,因?yàn)槲覀兗尤肓藸顟B(tài),也就意味我們是想通過狀態(tài)的變化來(lái)知道能不能得到值,那么問題來(lái)了,我們不知道狀態(tài)啥時(shí)候變,就像你不知道你要泡的妞啥時(shí)候答應(yīng)你一樣,你只能追問,萬(wàn)一妞沒想好,她很可能再給你一個(gè)承諾,就是那個(gè)該死的XXX時(shí)候再告訴你,不過好歹她也算給了你一個(gè)等待的機(jī)會(huì),而我們現(xiàn)在要做的就是創(chuàng)造這么個(gè)機(jī)會(huì)。
function Promise(fn) { var state = "pending"; var value; var deferred; function resolve(newValue) { value = newValue; state = "resolved"; if(deferred) { handle(deferred); } } function handle(onResolved) { if(state === "pending") { deferred = onResolved; return; } onResolved(value); } this.then = function(onResolved) { handle(onResolved); }; fn(resolve); }
這里引入了另外一個(gè)函數(shù)handle,至此可以說(shuō)promise的最關(guān)鍵的東西我們已經(jīng)看到了,妞的身材逐漸顯現(xiàn)。又扯遠(yuǎn)了?????
仔細(xì)看下除了handle我們還引入兩個(gè)變量value和deferred,先從最簡(jiǎn)單的來(lái):
value的作用很簡(jiǎn)單,在構(gòu)造函數(shù)內(nèi)它是一個(gè)全局變量,起到一個(gè)橋梁作用,就是為了在handle函數(shù)內(nèi)能取到newValue的值,而newValue就是fn函數(shù)里的那個(gè)結(jié)果。
handle我們估且可以認(rèn)為它是妞的一個(gè)管家,它會(huì)去替我們?cè)儐栨び袥]有想好,也就是去判斷當(dāng)前這個(gè)承諾的狀態(tài),再?zèng)Q定怎么做。
deferred我們估且可以這樣理解,它就是管家的一個(gè)記事本,你隔三差五的去問,它老人家不得記下來(lái),如果一不小心忘了,那就悲催了。
這下不管是同步還是異步,我們隨時(shí)可以在then方法中去取值,如果值沒有被resolve,也就是說(shuō)狀態(tài)沒發(fā)生變化,deferred將給我們記錄下這件事,等到resolve的那個(gè)時(shí)間點(diǎn)把值傳給then方法中那個(gè)回調(diào)函數(shù),onResolved。
在這里請(qǐng)默念一百遍handle,defer,再接著往下看,我保證他們會(huì)讓你困惑。
5、回到最初,為什么要用promise,想想回調(diào)地獄,再想想promise是怎么解決的,那就是then方法鏈?zhǔn)秸{(diào)用。
能夠?qū)崿F(xiàn)鏈?zhǔn)秸{(diào)用,也就是說(shuō)then方法返回的值也一定是個(gè)promise,這樣你才能.then,.then的一直寫下去。廢話不說(shuō),沒代碼說(shuō)個(gè)毛:
function Promise(fn) { var state = "pending"; var value; var deferred = null; function resolve(newValue) { value = newValue; state = "resolved"; if(deferred) { handle(deferred); } } function handle(handler) { if(state === "pending") { deferred = handler; return; } if(!handler.onResolved) { handler.resolve(value); return; } var ret = handler.onResolved(value); handler.resolve(ret); } this.then = function(onResolved) { return new Promise(function(resolve) { handle({ onResolved: onResolved, resolve: resolve }); }); }; fn(resolve); }
這下?lián)Q個(gè)姿勢(shì),我們先啃硬貨。我們讓then方法返回了一個(gè)promise,而且這個(gè)promise實(shí)例化時(shí)傳入的函數(shù)里調(diào)用了handle函數(shù),傳入了一個(gè)對(duì)象,onResolved很顯然就是then方法里第一個(gè)函數(shù),沒什么可說(shuō)的。關(guān)鍵是這handle和resolve是哪個(gè)?思考1分鐘。
這里我們用setTimeout簡(jiǎn)單模擬一個(gè)異步,拿一個(gè)then看下,發(fā)生了什么:
var promise = new Promise(function(resolve){ setTimeout(function(){ resolve(1); },3000) }); promise.then(function(res){ console.log(res); })
首先我們?nèi)ew一個(gè)promise,在實(shí)例化的過程中,調(diào)用了傳進(jìn)的那個(gè)函數(shù),3秒后才能執(zhí)行到resolve,緊接著調(diào)用了它的then方法,這個(gè)時(shí)候由于promise的狀態(tài)沒變,肯定取不到值,好在then方法會(huì)返回個(gè)promise,于是又執(zhí)行了一次promise的實(shí)例化過程。這里無(wú)法回避的就是作用域的問題,這個(gè)關(guān)系到handle函數(shù)執(zhí)行在哪個(gè)環(huán)境中,參數(shù)的到底從哪個(gè)地方獲取到,另外就是強(qiáng)大的閉包。相關(guān)知識(shí)不解釋。
為了看的更清楚,我們加入一些標(biāo)記,到chrome的控制臺(tái)中調(diào)試下:
var count = 0; function Promise(fn) { var state = "pending"; var value; var deferred = null; var scope = ++count; function resolve(newValue) { value = newValue; state = "resolved"; console.log("resolve: I am in " +scope); if(deferred) { handle(deferred); } } function handle(handler) { console.log("handle: I am in " +scope); if(state === "pending") { deferred = handler; return; } if(!handler.onResolved) { handler.resolve(value); return; } var ret = handler.onResolved(value); handler.resolve(ret); } this.then = function(onResolved) { console.log("then: I am in " + scope); return new Promise(function(resolve) { console.log("then promise: I am in " + scope); handle({ onResolved: onResolved, resolve: resolve }); }); }; fn(resolve); } var promise = new Promise(function(resolve){ setTimeout(function(){ resolve(1); },3000) }); promise.then(function(res){ console.log(res); });
加入的scope是為了監(jiān)視作用域的變化,以間接反應(yīng)出我們調(diào)用handle時(shí)是在哪個(gè)作用域上查詢到的,此外我們還需要監(jiān)視state和deferred的變化。
主要看then調(diào)用之后,廢話不說(shuō)上圖:
5-1、在執(zhí)行then方法的時(shí)候,scope=1,state,deferred不可用。由于模擬了異步,這個(gè)時(shí)候第一個(gè)promise的resolve方法并沒有執(zhí)行,這里模擬了3秒,實(shí)際情況下,比如ajax取數(shù)據(jù)時(shí),我們并不知道這個(gè)準(zhǔn)確的時(shí)間,就像開始時(shí)說(shuō)的,這妞啥時(shí)候答應(yīng)你,主動(dòng)權(quán)不在你手中,由妞說(shuō)了算。
5-2、接下來(lái)去實(shí)例化then方法創(chuàng)建的這個(gè)promise,scope = 2,state=”pending”,deferred=null。
5-3、在實(shí)例化完成之后,此時(shí)去執(zhí)行fn函數(shù),scope=1,state,deferred不可用。
第一,函數(shù)的作用域是在定義時(shí)就生成的,而不是在調(diào)用的時(shí)候。第二個(gè)promise定義的時(shí)候,是在第一個(gè)promise作用域上,這樣即使它被return了出去,由于閉包的特性,仍讀取的是第一個(gè)作用域上值,所以這里的handle必定是第一個(gè)promise的handle。而resolve則不同,它是作為行參傳遞了進(jìn)來(lái),所以這里的resolve是第二個(gè)promise的resolve。
5-4、進(jìn)入handle時(shí),scope = 1,state =” pending”,deferred保存了參數(shù)。
5-5、3秒時(shí)間到,第一個(gè)promise里的resolve被執(zhí)行了,也就是說(shuō)拿到了結(jié)果,這時(shí)候,scope=1,state = “resolved”,deferred保存著剛才傳進(jìn)來(lái)的那個(gè)對(duì)象,再次進(jìn)入handle函數(shù)。
5-6、scope=1,state = “resolved”,deferred求值為true,因此肯定會(huì)繼續(xù)執(zhí)行。下面添加的這段代碼在這里也就很清楚了,假如then方法中沒有傳進(jìn)來(lái)的onResolved函數(shù),這里的value將直接交給下一個(gè)then方法中的onResolved函數(shù)使用,避免一些無(wú)聊的人像這樣去調(diào)用:
promise.then().then().then(function(res){ console.log(res); })
正常人都會(huì)讓value在onResolved函數(shù)中接收到,然后ret就是onResolved函數(shù)的返回值,這里沒有return回的值,所以ret肯定是undefined。
5-7、scope=2,state = “resolved”,deferred=null。這里的resolve是第個(gè)promise的resolve,所以定義的時(shí)候就是在作用域2上,如果后面再調(diào)用then方法,生成新的promise,這時(shí)就會(huì)將undefined作為第二個(gè)promise的值傳遞下去。
這里再次強(qiáng)調(diào)一下,handle方法和deferred是核心所在,其背后的精髓無(wú)非還是作用域和閉包的巧妙設(shè)計(jì)。變量的讀取必定先從自身所處作用域開始,如果自身作用域上讀不到,才會(huì)一級(jí)一級(jí)向上訪問。
6、意淫到這里基本上核心的東西就差不多了,下面我們來(lái)加上reject時(shí)的情況,直接上代碼:
function Promise(fn) { var state = "pending"; var value; var deferred; this.then = function (onResolved, onRejected) { return new Promise(function (resolve, reject) { handle({ onResolved: onResolved, onRejected: onRejected, resolve: resolve, reject: reject }); }); }; function resolve(newValue) { if (newValue && typeof newValue.then === "function") { newValue.then(resolve); return; } state = "resolved"; value = newValue; if (deferred) { handle(deferred); } } function reject(reason) { state = "rejected"; value = reason; if (deferred) { handle(deferred); } } function handle(handler) { if (state === "pending") { deferred = handler; return; } var handlerCallback; if (state === "resolved") { handlerCallback = handler.onResolved; } else { handlerCallback = handler.onRejected; } if (!handlerCallback) { if (state === "resolved") { handler.resolve(value); } else { handler.reject(value); } return; } var ret; try { ret = handlerCallback(value); } catch (e) { handler.reject(e); return; } handler.resolve(ret); } fn(resolve); }
情況基本和resolve是一樣的,resolve函數(shù)中加的if判斷只為了對(duì)付返回值是promise的情況下仍然可以通過后續(xù)的then方法取到值,handle中的try/catch塊的加入使得可以捕獲到promise及then方法回調(diào)中的錯(cuò)誤,至于then方法的改變,看不懂的話自宮吧,你是女人當(dāng)我沒說(shuō)。
四、其它當(dāng)然這個(gè)promise只是一個(gè)基本的實(shí)現(xiàn),依然很脆弱,但基本上可以說(shuō)有了一輪廓,剩下的部位各位看官自己添加,比如promise的all ,race,catch等。某種意義上說(shuō),它們也只是then方法的語(yǔ)法糖。
http://www.mattgreer.org/arti...,本文代碼出處,算是學(xué)習(xí)后的一點(diǎn)體會(huì),水平有限,理解不對(duì)之處,還請(qǐng)指正。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/80872.html
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...
摘要:執(zhí)行的時(shí)候呢即可如何處理鏈?zhǔn)降那冶WC順序每個(gè)后面鏈一個(gè)對(duì)象該對(duì)象包含子三個(gè)屬性當(dāng)父狀態(tài)改變完畢執(zhí)行完相應(yīng)的的時(shí)候呢,拿到子在等待這個(gè)子狀態(tài)改變,在執(zhí)行相應(yīng)的。 promise源碼分析 初級(jí)入門以及如何使用請(qǐng)看 阮一峰promise對(duì)象講解 先上一坨代碼,后面我們要基于這坨代碼來(lái)實(shí)現(xiàn)自定義promise 原始方法 setTimeout(function(){ var a=100...
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:面試題來(lái)源于網(wǎng)絡(luò),看一下高級(jí)前端的面試題,可以知道自己和高級(jí)前端的差距。 面試題來(lái)源于網(wǎng)絡(luò),看一下高級(jí)前端的面試題,可以知道自己和高級(jí)前端的差距。有些面試題會(huì)重復(fù)。 使用過的koa2中間件 koa-body原理 介紹自己寫過的中間件 有沒有涉及到Cluster 介紹pm2 master掛了的話pm2怎么處理 如何和MySQL進(jìn)行通信 React聲明周期及自己的理解 如何...
摘要:今天對(duì)于處理異步調(diào)用已經(jīng)有了很多成熟的方案,在我看來(lái)這些方案都無(wú)外乎在解決一個(gè)問題如何能看似順序地傳遞異步調(diào)用的結(jié)果,本文要說(shuō)的就是原生提供的一個(gè)解決方案。在對(duì)進(jìn)行敘述之前,依舊引用阮大的入門一書中的章節(jié)便于大家更嚴(yán)謹(jǐn)和全面的學(xué)習(xí)和參考。 異步回調(diào)的泥潭 異步回調(diào)是最直接的異步結(jié)果處理模式,將一個(gè)回調(diào)函數(shù)callback扔進(jìn)異步處理函數(shù)中,當(dāng)異步處理獲得結(jié)果之后再調(diào)用這個(gè)回調(diào)函數(shù)就可以...
閱讀 2858·2021-11-22 13:54
閱讀 3522·2021-11-16 11:44
閱讀 1370·2021-09-07 10:19
閱讀 1470·2019-08-29 17:30
閱讀 3196·2019-08-29 11:33
閱讀 3543·2019-08-26 12:18
閱讀 2886·2019-08-26 11:53
閱讀 1336·2019-08-26 10:47