RN異步編程系列:ES6中Promise的用法

參考鏈接:
學習RxJS: 導入
http://www.moye.me/2016/05/31/learning_rxjs_part_one_preliminary/

RxJS中文文檔
https://cn.rx.js.org

Lodash中文文檔
https://www.lodashjs.com

阮一峰的ES6入門:Promise 對象
http://es6.ruanyifeng.com/#docs/promise

ES6關于Promise的用法
https://segmentfault.com/a/1190000011652907

ES6 Promise 用法(我見過最簡潔優秀的文章)
https://blog.csdn.net/shan1991fei/article/details/78966297


引子

新手們在異步編程里跌倒時,永遠會有這么一個經典問題:
怎么在一次異步調用里return一個結果啊?

老司機說要用【回調函數】,然后有條件判斷的嵌套回調(回調地獄)問題來了;

老司機推薦用【事件】,然后異步流程里有順序依賴;

老司機推薦用【Promise】,然后有順序依賴的流程里,居然還想訂閱事件;

老司機建議試試【協程】,誰知對方想要合并兩個異步調用;

……

以上,是異步編程里要面對的一些難題,也是【ReactiveX API】 所致力解決的


一、Promise的用法

  1. Promise 的含義
  2. 基本用法
  3. Promise.prototype.then()
  4. Promise.prototype.catch()
  5. Promise.prototype.finally()
  6. Promise.all()
  7. Promise.race()
  8. Promise.resolve()
  9. Promise.reject()
  10. 應用
  11. Promise.try()

Promise 的含義

Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最早提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。

所謂Promise
簡單說就是一個容器,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果。
從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。

Promise對象有以下兩個特點。
(1)對象的狀態不受外界影響。Promise對象代表一個異步操作,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。

(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。
Promise對象的狀態改變,只有兩種可能:
pending變為fulfilled
pending變為rejected
只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。
如果改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。

注意,為了行文方便,本章后面的resolved統一只指fulfilled狀態,不包含rejected狀態。

有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操作更加容易。

Promise也有一些缺點。
1、無法取消Promise,一旦新建它就會立即執行,無法中途取消。
2、如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。
3、當處于pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

如果某些事件不斷地反復發生,一般來說,使用 Stream 模式是比部署Promise更好的選擇。


如果我們有幾個異步操作,并且后一個操作需要前一個操作返回的數據才能執行,這樣按照Node的一般執行規律,要實現有序的異步操作,通常是一層加一層嵌套下去。

為了解決這個問題,ES6提出了Promise的實現。

含義
Promise 對象用于一個異步操作的最終完成(或失敗)及其結果值的表示。
簡單點說,它就是用于處理異步操作的,異步處理成功了就執行成功的操作,異步處理失敗了就捕獲錯誤或者停止后續操作。


例子

var promise1 = new Promise(function(resolve, reject) {
  // 2秒后置為接收狀態
  setTimeout(function() {
    resolve('success');
  }, 2000);
});

promise1.then(function(data) {
  console.log(data); // success
}, function(err) {
  console.log(err); // 不執行
}).then(function(data) {
  // 上一步的then()方法沒有返回值
  console.log('鏈式調用:' + data); // 鏈式調用:undefined 
}).then(function(data) {
  // ....
});

在這里我們主要關注promise1.then()方法調用后返回的Promise對象的狀態,是pending還是fulfilled,或者是rejected?

返回的這個Promise對象的狀態主要是根據promise1.then()方法返回的值,大致分為以下幾種情況:
如果then()方法中返回了一個參數值,那么返回的Promise將會變成接收狀態。
如果then()方法中拋出了一個異常,那么返回的Promise將會變成拒絕狀態。
如果then()方法調用resolve()方法,那么返回的Promise將會變成接收狀態。
如果then()方法調用reject()方法,那么返回的Promise將會變成拒絕狀態。
如果then()方法返回了一個未知狀態(pending)的Promise新實例,那么返回的新Promise就是未知狀態。
如果then()方法沒有明確指定的resolve(data)/reject(data)/return data時,那么返回的新Promise就是接收狀態,可以一層一層地往下傳遞。

轉換實例如下:

var promise2 = new Promise(function(resolve, reject) {
  // 2秒后置為接收狀態
  setTimeout(function() {
    resolve('success');
  }, 2000);
});

promise2
  .then(function(data) {
    // 上一個then()調用了resolve,置為fulfilled態
    console.log('第一個then');
    console.log(data);
    return '2';
  })
  .then(function(data) {
    // 此時這里的狀態也是fulfilled, 因為上一步返回了2
    console.log('第二個then');
    console.log(data);  // 2

    return new Promise(function(resolve, reject) {
      reject('把狀態置為rejected error'); // 返回一個rejected的Promise實例
    });
  }, function(err) {
    // error
  })
  .then(function(data) {
    /* 這里不運行 */
    console.log('第三個then');
    console.log(data);
    // ....
  }, function(err) {
    // error回調
    // 此時這里的狀態也是fulfilled, 因為上一步使用了reject()來返回值
    console.log('出錯:' + err); // 出錯:把狀態置為rejected error
  })
  .then(function(data) {
    // 沒有明確指定返回值,默認返回fulfilled
    console.log('這里是fulfilled態');
});

下面代碼中,booksPromise和userPromise是兩個異步操作,只有等到它們的結果都返回了,才會觸發pickTopRecommentations這個回調函數。

const databasePromise = connectDatabase();

const booksPromise = databasePromise
  .then(findAllBooks);

const userPromise = databasePromise
  .then(getCurrentUser);

Promise.all([
  booksPromise,
  userPromise
])
.then(([books, user]) => pickTopRecommentations(books, user));


下面代碼中,如果 5 秒之內fetch方法無法返回結果,變量p的狀態就會變為rejected,從而觸發catch方法指定的回調函數。

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容