ES6 Promise:模式與反模式

原文:ES6 Promises: Patterns and Anti-Patterns
作者:Bobby Brennan

當(dāng)幾年前厂汗,第一次使用 NodeJS 的時(shí)候,對(duì)現(xiàn)在被稱為“ 回調(diào)地獄 ”的寫法感到很困擾呜师。幸運(yùn)的是娶桦,現(xiàn)在是 2017 年了,NodeJS 已經(jīng)采用大量 JavaScript 的最新特性,從 v4 開始已經(jīng)支持 Promise衷畦。

盡管 Promise 可以讓代碼更加簡(jiǎn)潔易讀栗涂,但對(duì)于只熟悉回調(diào)函數(shù)的人來說,可能對(duì)此還是會(huì)有所懷疑祈争。在這里斤程,將列出我在使用Promise 時(shí)學(xué)到的一些基本模式,以及踩的一些坑菩混。

注意:在本文中將使用箭頭函數(shù) 忿墅,如果你還不是很熟悉,其實(shí)很簡(jiǎn)單沮峡,建議先讀一下使用它們的好處

模式與最佳實(shí)踐

使用 Promise

如果使用的是已經(jīng)支持 Promise 的第三方庫(kù)疚脐,那么使用起來非常簡(jiǎn)單。只需關(guān)心兩個(gè)函數(shù):then()catch()帖烘。例如亮曹,有一個(gè)客戶端 API 包含三個(gè)方法橄杨,getItem()秘症,updateItem(),和deleteItem()式矫,每一個(gè)方法都返回一個(gè) Promise:

Promise.resolve()
  .then(_ => {
    return api.getItem(1)
  })
  .then(item => {
    item.amount++
    return api.updateItem(1, item);
  })
  .then(update => {
    return api.deleteItem(1);
  })
  .catch(e => {
    console.log('error while working on item 1');
  })

每次調(diào)用 then() 會(huì)在 Promise 鏈中創(chuàng)建一個(gè)新的步驟乡摹,如果鏈中的任何一個(gè)地方出現(xiàn)錯(cuò)誤,就會(huì)觸發(fā)接下來的 catch() 采转。then()catch() 都可以返回一個(gè)值或者一個(gè)新的 Promise聪廉,結(jié)果將被傳遞到 Promise 鏈的下一個(gè)then()

為了比較故慈,這里使用回調(diào)函數(shù)來實(shí)現(xiàn)相同邏輯:

api.getItem(1, (err, data) => {
  if (err) throw err;
  item.amount++;
  api.updateItem(1, item, (err, update) => {
    if (err) throw err;
    api.deleteItem(1, (err) => {
      if (err) throw err;
    })
  })
})

要注意的第一個(gè)區(qū)別是板熊,使用回調(diào)函數(shù),我們必須在過程的每個(gè)步驟中進(jìn)行錯(cuò)誤處理察绷,而不是用單個(gè)的 catch-all 來處理干签。回調(diào)函數(shù)的第二個(gè)問題更直觀拆撼,每個(gè)步驟都要水平縮進(jìn)容劳,而使用 Promise 的代碼則有顯而易見的順序關(guān)系。

回調(diào)函數(shù) Promise 化

需要學(xué)習(xí)的第一個(gè)技巧是如何將回調(diào)函數(shù)轉(zhuǎn)換為 Promise闸度。你可能正在使用仍然基于回調(diào)的庫(kù)竭贩,或是自己的舊代碼,不過不用擔(dān)心莺禁,因?yàn)橹恍枰獛仔写a就可以將其包裝成一個(gè) Promise留量。這是將 Node 中的一個(gè)回調(diào)方法 fs.readFile 轉(zhuǎn)換為 Promise的示例:

function readFilePromise(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(filename, 'utf8', (err, data) => {
      if (err) reject(err);
      else resolve(data);
    })
  })
}

readFilePromise('index.html')
  .then(data => console.log(data))
  .catch(e => console.log(e))

關(guān)鍵部分是 Promise 構(gòu)造函數(shù),它接收一個(gè)函數(shù)作為參數(shù),這個(gè)函數(shù)有兩個(gè)函數(shù)參數(shù):resolvereject肪获。在這個(gè)函數(shù)里完成所有工作寝凌,完成之后,在成功時(shí)調(diào)用 resolve孝赫,如果有錯(cuò)誤則調(diào)用 reject较木。

需要注意的是只有一個(gè)resolve 或者 reject 被調(diào)用,即應(yīng)該只被調(diào)用一次青柄。在我們的示例中伐债,如果 fs.readFile 返回錯(cuò)誤,我們將錯(cuò)誤傳遞給 reject,否則將文件數(shù)據(jù)傳遞給resolve脱拼。

Promise 的值

ES6 有兩個(gè)很方便的輔助函數(shù)回还,用于通過普通值創(chuàng)建 Promise:Promise.resolve()Promise.reject()。例如虹蒋,可能需要在同步處理某些情況時(shí)一個(gè)返回 Promise 的函數(shù):

function readFilePromise(filename) {
  if (!filename) {
    return Promise.reject(new Error("Filename not specified"));
  }
  if (filename === 'index.html') {
    return Promise.resolve('<h1>Hello!</h1>');
  }
  return new Promise((resolve, reject) => {/*...*/})
}

注意,雖然可以傳遞任何東西(或者不傳遞任何值)給 Promise.reject()飒货,但是好的做法是傳遞一個(gè)Error魄衅。

并行運(yùn)行

Promise.all是一個(gè)并行運(yùn)行 Promise 數(shù)組的方法,也就是說是同時(shí)運(yùn)行塘辅。例如晃虫,我們有一個(gè)要從磁盤讀取文件的列表。使用上面創(chuàng)建的 readFilePromise 函數(shù)扣墩,將如下所示:

let filenames = ['index.html', 'blog.html', 'terms.html'];

Promise.all(filenames.map(readFilePromise))
  .then(files => {
    console.log('index:', files[0]);
    console.log('blog:', files[1]);
    console.log('terms:', files[2]);
  })

我甚至不會(huì)使用傳統(tǒng)的回調(diào)函數(shù)來嘗試編寫與之等效的代碼哲银,那樣會(huì)很凌亂,而且也容易出錯(cuò)呻惕。

串行運(yùn)行

有時(shí)同時(shí)運(yùn)行一堆 Promise 可能會(huì)出現(xiàn)問題荆责。比如,如果嘗試使用 Promise.all 的 API ??去檢索一堆資源亚脆,則可能會(huì)在達(dá)到速率限制時(shí)開始響應(yīng)429錯(cuò)誤做院。

一種解決方案是串行運(yùn)行 Promise,或一個(gè)接一個(gè)地運(yùn)行型酥。但是在 ES6 中沒有提供類似 Promise.all 這樣的方法(為什么山憨?),但我們可以使用 Array.reduce 來實(shí)現(xiàn):

let itemIDs = [1, 2, 3, 4, 5];

itemIDs.reduce((promise, itemID) => {
  return promise.then(_ => api.deleteItem(itemID));
}, Promise.resolve());

在這種情況下弥喉,我們需要等待每次調(diào)用 api.deleteItem() 完成之后才能進(jìn)行下一次調(diào)用郁竟。這種方法,比為每個(gè) itemID 寫 .then() 更簡(jiǎn)潔更通用:

Promise.resolve()
  .then(_ => api.deleteItem(1))
  .then(_ => api.deleteItem(2))
  .then(_ => api.deleteItem(3))
  .then(_ => api.deleteItem(4))
  .then(_ => api.deleteItem(5));

Race

ES6 提供的另一個(gè)很方便的函數(shù)是 Promise.race由境。跟 Promise.all 一樣棚亩,接收一個(gè) Promise 數(shù)組蓖议,并同時(shí)運(yùn)行它們,但不同的是讥蟆,會(huì)在一旦任何 Promise 完成或失敗的情況下返回勒虾,并放棄所有其他的結(jié)果。

例如瘸彤,我們可以創(chuàng)建一個(gè)在幾秒鐘之后超時(shí)的 Promise:

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(reject, ms);
  })
}

Promise.race([readFilePromise('index.html'), timeout(1000)])
  .then(data => console.log(data))
  .catch(e => console.log("Timed out after 1 second"))

需要注意的是修然,其他 Promise 仍將繼續(xù)運(yùn)行 ,只是看不到結(jié)果而已质况。

捕獲錯(cuò)誤

捕獲錯(cuò)誤最常見的方式是添加一個(gè) .catch() 代碼塊愕宋,這將捕獲前面所有 .then() 代碼塊中的錯(cuò)誤 :

Promise.resolve()
  .then(_ => api.getItem(1))
  .then(item => {
    item.amount++;
    return api.updateItem(1, item);
  })
  .catch(e => {
    console.log('failed to get or update item');
  })

在這里,只要有 getItem 或者 updateItem 失敗结榄,catch()就會(huì)被觸發(fā)中贝。但是如果我們想分開處理 getItem 的錯(cuò)誤怎么辦?只需再插入一個(gè)catch() 就可以臼朗,它也可以返回另一個(gè) Promise邻寿。

Promise.resolve()
  .then(_ => api.getItem(1))
  .catch(e => api.createItem(1, {amount: 0}))
  .then(item => {
    item.amount++;
    return api.updateItem(1, item);
  })
  .catch(e => {
    console.log('failed to update item');
  })

現(xiàn)在,如果getItem()失敗视哑,我們通過第一個(gè) catch 介入并創(chuàng)建一條新的記錄绣否。

拋出錯(cuò)誤

應(yīng)該將 then() 語(yǔ)句中的所有代碼視為 try 塊內(nèi)的所有代碼。return Promise.reject()throw new Error() 都會(huì)導(dǎo)致下一個(gè) catch() 代碼塊的運(yùn)行黎炉。

這意味著運(yùn)行時(shí)錯(cuò)誤也會(huì)觸發(fā) catch()枝秤,所以不要去假設(shè)錯(cuò)誤的來源醋拧。例如慷嗜,在下面的代碼中,我們可能希望該 catch() 只能獲得 getItem 拋出的錯(cuò)誤丹壕,但是如示例所示庆械,它還會(huì)在我們的 then() 語(yǔ)句中捕獲運(yùn)行時(shí)錯(cuò)誤。

api.getItem(1)
  .then(item => {
    delete item.owner;
    console.log(item.owner.name);
  })
  .catch(e => {
    console.log(e); // Cannot read property 'name' of undefined
  })

動(dòng)態(tài)鏈

有時(shí)菌赖,我們想要?jiǎng)討B(tài)地構(gòu)建 Promise 鏈缭乘,例如,在滿足特定條件時(shí)琉用,插入一個(gè)額外的步驟堕绩。在下面的示例中,在讀取給定文件之前邑时,我們可以選擇創(chuàng)建一個(gè)鎖定文件:

function readFileAndMaybeLock(filename, createLockFile) {
  let promise = Promise.resolve();

  if (createLockFile) {
    promise = promise.then(_ => writeFilePromise(filename + '.lock', ''))
  }

  return promise.then(_ => readFilePromise(filename));
}

一定要通過重寫 promise = promise.then(/*...*/) 來更新 Promise 的值奴紧。參看接下來反模式中會(huì)提到的 多次調(diào)用 then()

反模式

Promise 是一個(gè)整潔的抽象晶丘,但很容易陷入某些陷阱黍氮。以下是我遇到的一些最常見的問題唐含。

重回回調(diào)地獄

當(dāng)我第一次從回調(diào)函數(shù)轉(zhuǎn)到 Promise 時(shí),發(fā)現(xiàn)很難擺脫一些舊習(xí)慣沫浆,仍像使用回調(diào)函數(shù)一樣嵌套 Promise:

api.getItem(1)
  .then(item => {
    item.amount++;
    api.updateItem(1, item)
      .then(update => {
        api.deleteItem(1)
          .then(deletion => {
            console.log('done!');
          })
      })
  })

這種嵌套是完全沒有必要的捷枯。有時(shí)一兩層嵌套可以幫助組合相關(guān)任務(wù),但是最好總是使用 .then() 重寫成 Promise 垂直鏈 专执。

沒有返回

我遇到的一個(gè)經(jīng)常會(huì)犯的錯(cuò)誤是在一個(gè) Promise 鏈中忘記 return 語(yǔ)句淮捆。你能發(fā)現(xiàn)下面的 bug 嗎?

api.getItem(1)
  .then(item => {
    item.amount++;
    api.updateItem(1, item);
  })
  .then(update => {
    return api.deleteItem(1);
  })
  .then(deletion => {
    console.log('done!');
  })

因?yàn)槲覀儧]有在第4行的 api.updateItem() 前面寫 return本股,所以 then() 代碼塊會(huì)立即 resolove争剿,導(dǎo)致 api.deleteItem() 可能在api.updateItem() 完成之前就被調(diào)用。

在我看來痊末,這是 ES6 Promise 的一個(gè)大問題蚕苇,往往會(huì)引發(fā)意想不到的行為。問題是凿叠, .then() 可以返回一個(gè)值涩笤,也可以返回一個(gè)新的 Promise,undefined 完全是一個(gè)有效的返回值盒件。就個(gè)人而言蹬碧,如果我負(fù)責(zé) Promise API,我會(huì)在 .then() 返回 undefined 時(shí)拋出運(yùn)行時(shí)錯(cuò)誤炒刁,但現(xiàn)在我們需要特別注意 return 創(chuàng)建的 Promise恩沽。

多次調(diào)用 .then()

根據(jù)規(guī)范,在同一個(gè) Promise 上多次調(diào)用 then() 是完全有效的翔始,并且回調(diào)將按照其注冊(cè)順序被調(diào)用罗心。但是,我并未見過需要這樣做的場(chǎng)景城瞎,并且在使用返回值和錯(cuò)誤處理時(shí)可能會(huì)產(chǎn)生一些意外行為:

let p = Promise.resolve('a');
p.then(_ => 'b');
p.then(result => {
  console.log(result) // 'a'
})

let q = Promise.resolve('a');
q = q.then(_ => 'b');
q = q.then(result => {
  console.log(result) // 'b'
})

在這個(gè)例子中渤闷,因?yàn)槲覀冊(cè)诿看握{(diào)用 then() 不更新 p 的值,所以我們看不到 'b' 返回脖镀。但是每次調(diào)用 then() 時(shí)更新 q飒箭,所以其行為更可預(yù)測(cè)。

這也適用于錯(cuò)誤處理:

let p = Promise.resolve();
p.then(_ => {throw new Error("whoops!")})
p.then(_ => {
  console.log('hello!'); // 'hello!'
})

let q = Promise.resolve();
q = q.then(_ => {throw new Error("whoops!")})
q = q.then(_ => {
  console.log('hello'); // We never reach here
})

在這里蜒灰,我們期望的是拋出一個(gè)錯(cuò)誤來打破 Promise 鏈弦蹂,但由于沒有更新 p 的值,所以第二個(gè) then() 仍會(huì)被調(diào)用强窖。

有可能在一個(gè) Promise 上多次調(diào)用 .then() 有很多理由 凸椿,因?yàn)樗试S將 Promise 分配到幾個(gè)新的獨(dú)立的 Promise 中,但是還沒發(fā)現(xiàn)真實(shí)的使用場(chǎng)景毕骡。

混合使用回調(diào)和 Promise

很容易進(jìn)入一種陷阱削饵,在使用基于 Promise 庫(kù)的同時(shí)岩瘦,仍在基于回調(diào)的項(xiàng)目中工作。始終避免在 then()catch() 使用回調(diào)函數(shù)?窿撬,否則 Promise 會(huì)吞噬任何后續(xù)的錯(cuò)誤启昧,將其作為 Promise 鏈的一部分。例如劈伴,以下內(nèi)容看起來是一個(gè)挺合理的方式密末,使用回調(diào)函數(shù)來包裝一個(gè) Promise:

function getThing(callback) {
  api.getItem(1)
    .then(item => callback(null, item))
    .catch(e => callback(e));
}

getThing(function(err, thing) {
  if (err) throw err;
  console.log(thing);
})

這里的問題是,如果有錯(cuò)誤跛璧,我們會(huì)收到關(guān)于“Unhandled promise rejection”的警告严里,即使我們添加了一個(gè) catch() 代碼塊。這是因?yàn)椋?code>callback() 在 then()catch() 都會(huì)被調(diào)用追城,使之成為 Promise 鏈的一部分刹碾。

如果必須使用回調(diào)來包裝 Promise,可以使用 setTimeout (或者是 NodeJS 中的 process.nextTick)來打破 Promise:

function getThing(callback) {
  api.getItem(1)
    .then(item => setTimeout(_ => callback(null, item)))
    .catch(e => setTimeout(_ => callback(e)));
}

getThing(function(err, thing) {
  if (err) throw err;
  console.log(thing);
})

不捕獲錯(cuò)誤

JavaScript 中的錯(cuò)誤處理有點(diǎn)奇怪座柱。雖然支持熟悉的 try/catch 范例迷帜,但是沒有辦法強(qiáng)制調(diào)用者以 Java 的方式處理錯(cuò)誤。然而色洞,使用回調(diào)函數(shù)戏锹,使用所謂的“errbacks”,即第一個(gè)參數(shù)是一個(gè)錯(cuò)誤回調(diào)變得很常見火诸。這迫使調(diào)用者至少承認(rèn)錯(cuò)誤的可能性锦针。例如,fs 庫(kù):

fs.readFile('index.html', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
})

使用 Promise置蜀,又將很容易忘記需要進(jìn)行錯(cuò)誤處理奈搜,特別是對(duì)于敏感操作(如文件系統(tǒng)和數(shù)據(jù)庫(kù)訪問)。目前盾碗,如果沒有捕獲到 reject 的 Promise媚污,將在 NodeJS 中看到非常丑的警告:

(node:29916) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: whoops!
(node:29916) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

確保在主要的事件循環(huán)中任何 Promise 鏈的末尾添加 catch() 以避免這種情況舀瓢。

總結(jié)

希望這是一篇有用的關(guān)于常見 Promise 模式和反模式的概述廷雅。如果你想了解更多,這里有一些有用的資源:

更多的 Promise 模式反模式

或者閱讀來自 DataFire 團(tuán)隊(duì)的內(nèi)容

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末京髓,一起剝皮案震驚了整個(gè)濱河市航缀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌堰怨,老刑警劉巖芥玉,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異备图,居然都是意外死亡灿巧,警方通過查閱死者的電腦和手機(jī)赶袄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抠藕,“玉大人饿肺,你說我怎么就攤上這事《芩疲” “怎么了敬辣?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)零院。 經(jīng)常有香客問我溉跃,道長(zhǎng),這世上最難降的妖魔是什么告抄? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任撰茎,我火速辦了婚禮,結(jié)果婚禮上打洼,老公的妹妹穿的比我還像新娘乾吻。我一直安慰自己,他們只是感情好拟蜻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布绎签。 她就那樣靜靜地躺著,像睡著了一般酝锅。 火紅的嫁衣襯著肌膚如雪诡必。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天搔扁,我揣著相機(jī)與錄音爸舒,去河邊找鬼。 笑死稿蹲,一個(gè)胖子當(dāng)著我的面吹牛扭勉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苛聘,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼涂炎,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了设哗?” 一聲冷哼從身側(cè)響起唱捣,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎网梢,沒想到半個(gè)月后震缭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡战虏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年拣宰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了党涕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡巡社,死狀恐怖遣鼓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情重贺,我是刑警寧澤骑祟,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站气笙,受9級(jí)特大地震影響次企,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜潜圃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一缸棵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谭期,春花似錦堵第、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至胀瞪,卻和暖如春针余,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凄诞。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工圆雁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帆谍。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓伪朽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親汛蝙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子烈涮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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

  • Promiese 簡(jiǎn)單說就是一個(gè)容器,里面保存著某個(gè)未來才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果患雇,語(yǔ)法上說跃脊,Pr...
    雨飛飛雨閱讀 3,358評(píng)論 0 19
  • 本文適用的讀者 本文寫給有一定Promise使用經(jīng)驗(yàn)的人,如果你還沒有使用過Promise苛吱,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,310評(píng)論 6 19
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持器瘪,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券翠储,享受所有官網(wǎng)優(yōu)惠绘雁,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 11,026評(píng)論 26 95
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,170評(píng)論 0 16
  • 順著陰暗的樓道前行援所,四周都是灰土庐舟。另外有幾個(gè)人,各自走路住拭。一個(gè)既喜悅又哀傷的聲音在耳邊響起“完了挪略,我考砸了...
    the_loner閱讀 154評(píng)論 0 0