自己動手實現(xiàn)ES6 Promise

不管是標準還是實現(xiàn)损同,現(xiàn)在Javascript的重心都放在了async-await上,Promise怎么看都像過時的東西。而且支持Promise的庫有一大堆禾锤,就算不需要這些庫,今天的瀏覽器和Node.js也已經(jīng)原生支持Promise了摹察。在這種前提下恩掷,為什么還要自己去實現(xiàn)一個Promise呢?

ES7的async-await建立在Promise上

客觀來講供嚎,由于await本身的特點黄娘,將來JS把底層API良好封裝之后,即使用戶完全不知道Promise克滴,在使用上也不會有啥問題~

然而ES7的Async-Await逼争,和Promise并不是毫不相關的競爭對手。實際上await后面必須跟著一個Promise對象劝赔。所以深入理解Promise不會是毫無用處的事情誓焦。

Promise不需要編譯器/解釋器的支持

將來可能成為主流的async-await,以及曾經(jīng)火過一把的generator + co着帽,這些都是需要編譯器或者解釋器級別的支持才能使用杂伟。

而Promise,是完全可以利用語言已有特性仍翰,作為一個庫來實現(xiàn)赫粥!即使在非常原始的JS運行環(huán)境,你也可以自己實現(xiàn)一個Promise予借,而不需要等待其他人的幫助越平。

Promise是語言無關的

Promise還是獨立于語言的,如果你要給另外一種編程語言實現(xiàn)Promise灵迫,只要照葫蘆畫瓢就行了喧笔。

也就是說,掌握Promise的實現(xiàn)原理龟再,是一種回報率非常高的通用型技能书闸。而且只需要很少的投入,一百多行代碼而已利凑。(核心的其實只有幾十行)

所以浆劲,讓我們開始吧嫌术!

如何實現(xiàn)

對于Promise這種代碼量不大,但是行為復雜的程序牌借,最好的學習方法是直接看代碼度气。只看別人的解釋可能會弄得似懂非懂。

先放一個我自己的實現(xiàn)以供參考膨报,建議還在網(wǎng)上搜一下其他人的實現(xiàn)磷籍。很多論壇里面都有人寫簡單的部分Promise實現(xiàn),大都有借鑒價值现柠。通過看不同人的寫法院领,可以更容易理解其核心部分。我在實現(xiàn)自己的Promise時够吩,就看過了很多片段代碼比然,然后才慢慢知道該怎么做。

最核心的方法

最核心的一個方法是Promise.prototype.then周循,實現(xiàn)了它强法,也就實現(xiàn)了Promise的一大半。如果再把Promise.allPromise.race實現(xiàn)了湾笛,你基本上就實現(xiàn)了完整的Promise饮怯,因為剩下的,都是簡單的封裝而已嚎研。

then

每次調(diào)用then硕淑,你都在創(chuàng)建一個新的Promise對象。then就像一個鎖鏈一樣嘉赎,將前后的兩個Promise對象連接起來置媳。

為了突出這一點,我在自己的實現(xiàn)里面公条,特意把邏輯代碼外移拇囊,下面是代碼片段

Promise.prototype.then = function(resolveFn, rejectFn) {
  var pP = this
  return new Promise((res, rej) => thenHandler(res, rej, resFn, rejFn, pP))
}

all

調(diào)用Promise.all,也會返回一個新的Promise對象靶橱,all后面的then寥袭,是掛在這個新的Promise對象上的。

Promise.all = function(promises) {
  checkArray(promises)
  return new Promise((res, rej) => handleAll(promises, res, rej))
}

pending, fulfilled, rejected

要理解Promise关霸,另一個關鍵在于理解Promise的「狀態(tài)」传黄。一個Promise對象是有三種狀態(tài)的, pending队寇,fulfilledrejected膘掰。為什么要有狀態(tài)?為了分情況處理。

先舉一個例子识埋,假如我定義一個Proimse凡伊,但是不給它綁定then

var x = myReadFile("/tmp/text.txt")

這條語句在運行的時候,那個文件的內(nèi)容其實已經(jīng)在某個時間點讀出來了窒舟。一直緩存在某個地方系忙。吃完飯我們再來運行:

x.then(console.log)
//> Promise { <pending> }
//  blahblah..., the content of /tmp/text.txt

數(shù)據(jù)全都出來了!因為then會對Promise對象的「狀態(tài)」進行判斷惠豺。如果是pending狀態(tài)银还,就把將要運行的函數(shù)存到Promise對象的一個數(shù)組里面,如果是fulfilled狀態(tài)洁墙,也就是那個Promise的resolve已經(jīng)被運行了蛹疯,那么就直接調(diào)用then傳來的函數(shù)。

這就是狀態(tài)存在的意義扫俺。

然后就可以直接看代碼了苍苞,需要的就是適當?shù)哪托墓毯玻瑯酚^的態(tài)度~

如果讀者對Promise不了解狼纬,想知道它運行的一些特點,那么可以繼續(xù)往下看骂际。

Promise的運行機理

接下來疗琉,我們通過兩個例子來探索Promise的運行過程,為了減少重復代碼歉铝,我們先定義一個函數(shù)盈简,這個函數(shù)會返回一個Promise對象,這就是一切的開端太示。

function myReadFile(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(filename, (e, d) => e ? reject(e) : resolve(d.toString()))
  })
}

使用Promise柠贤,代碼經(jīng)常看上去會是這個樣子的

myReadFile("theFirst.json")
.then(JSON.parse)
.then(fn1)
.then(fn2)
.then(fn3)
.catch(console.error.bind(console))

上面這個例子中类缤,真正能夠異步的臼勉,只有第一步而已。一旦那個resolve被調(diào)用餐弱,后面的一連串都會順著執(zhí)行宴霸。就像多米諾骨牌一樣。

那么如果中間有另一個地方需要異步怎么辦呢膏蚓,比如我需要讀取另外一個文件瓢谢? 你只需要在某個傳給then的函數(shù)里面,返回一個新的Promise對象就行驮瞧。

myReadFile("theFirst.json")
.then(JSON.parse)
.then(fn1)
.then(d => myReadFile("theSecond.json"))
.then(JSON.parse)
.then(fn3)
.catch(console.error.bind(console))

上面這個例子中氓扛,fn3處理的是theSecond.json文件的內(nèi)容。

這一點可能理解上有點別扭论笔,大概需要實現(xiàn)了Promise之后幢尚,才能清楚其中的貓膩破停。

關于Promise,我能想到的需要注意的尉剩,暫時就這么多了真慢。以后如果有新的點子,會繼續(xù)放上來理茎。

Promise的缺點

我對Promise非常喜愛黑界,但是它的確有缺點,比如一些中間變量無法共用皂林,我們拿同步例子來做個對比

var a = readFileSync("blahblah.txt")
var b = fn1(a)
var c = fn2(b)
var d = fn3(c)
console.log(a, b, c, d)

而使用Promise的異步則是這個情況

myReadFile("blahblah.txt")
.then(fn1)
.then(fn2)
.then(fn3)
.then(d => console.log(d))

其中fn1的返回值只有fn2能獲取朗鸠,fn2的返回值只有fn3能獲取…… 如果需要像同步版本那樣,獲取所有中間值础倍,就必須把它們存為全局或者上層閉包變量烛占。

但是這個小小的缺點不太要緊。瑕不掩瑜沟启,Promise徹底解決了callback hell忆家,讓我對Javascript另眼相看。

后記

隨著對Promise使用時間的增長德迹,我意識到了Promise的一些其他優(yōu)點芽卿。它不僅僅是解決了回調(diào)的“代碼金字塔”問題。應該說胳搞,回調(diào)帶來的真正問題卸例,并不是代碼不停往右邊延展,而是你不能以正常的「函數(shù)」概念來思考問題肌毅。

什么是函數(shù)筷转?我記得以前數(shù)學老師向我們解釋:函數(shù)就是一個工廠,你給一個毛胚進去悬而,它變出一個產(chǎn)品呜舒。

當時我還沒有編程的概念,當我學會編程之后摊滔,更加贊同那個樸實的比喻了阴绢。函數(shù)這東西,就是做轉(zhuǎn)換艰躺,它不僅要有輸入呻袭,還要有輸出。

而回調(diào)腺兴,是“沒有”輸出的左电。

它當然有輸出,只不過很別扭,因為它不是作為返回值呈現(xiàn)篓足,而是通過你傳給它的另一個輸入(回調(diào)函數(shù))來處理段誊。是不是隱約找到當年C語言那堆庫函數(shù)在你心中留下的傷疤。

Promise重新賦予了我們“正痴煌希”的函數(shù)连舍,這是它更重要的意義。

原文:http://madmuggle.me/articles/ES6_Promise.html

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涩哟,一起剝皮案震驚了整個濱河市索赏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贴彼,老刑警劉巖潜腻,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異器仗,居然都是意外死亡融涣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門精钮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來威鹿,“玉大人,你說我怎么就攤上這事杂拨∽ㄆ眨” “怎么了悯衬?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵弹沽,是天一觀的道長。 經(jīng)常有香客問我筋粗,道長策橘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任娜亿,我火速辦了婚禮丽已,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘买决。我一直安慰自己沛婴,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布督赤。 她就那樣靜靜地躺著嘁灯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪躲舌。 梳的紋絲不亂的頭發(fā)上丑婿,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音,去河邊找鬼羹奉。 笑死秒旋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的诀拭。 我是一名探鬼主播迁筛,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耕挨!你這毒婦竟也來了瑰煎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤俗孝,失蹤者是張志新(化名)和其女友劉穎酒甸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赋铝,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡插勤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了革骨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片农尖。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖良哲,靈堂內(nèi)的尸體忽然破棺而出盛卡,到底是詐尸還是另有隱情,我是刑警寧澤筑凫,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布滑沧,位于F島的核電站,受9級特大地震影響巍实,放射性物質(zhì)發(fā)生泄漏滓技。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一棚潦、第九天 我趴在偏房一處隱蔽的房頂上張望令漂。 院中可真熱鬧,春花似錦丸边、人聲如沸叠必。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纬朝。三九已至,卻和暖如春嘱吗,著一層夾襖步出監(jiān)牢的瞬間玄组,已是汗流浹背滔驾。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俄讹,地道東北人哆致。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像患膛,于是被迫代替她去往敵國和親摊阀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 異步編程對JavaScript語言太重要踪蹬。Javascript語言的執(zhí)行環(huán)境是“單線程”的胞此,如果沒有異步編程,根本...
    呼呼哥閱讀 7,298評論 5 22
  • Promiese 簡單說就是一個容器跃捣,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果漱牵,語法上說,Pr...
    雨飛飛雨閱讀 3,348評論 0 19
  • Promise的含義: ??Promise是異步編程的一種解決方案疚漆,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,164評論 0 16
  • 弄懂js異步 講異步之前酣胀,我們必須掌握一個基礎知識-event-loop。 我們知道JavaScript的一大特點...
    DCbryant閱讀 2,697評論 0 5
  • 00、前言Promise 是異步編程的一種解決方案丸升,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大铆农。它由社區(qū)...
    夜幕小草閱讀 2,128評論 0 12