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 用法(我見過最簡潔優(yōu)秀的文章)
https://blog.csdn.net/shan1991fei/article/details/78966297


引子

新手們在異步編程里跌倒時,永遠會有這么一個經(jīng)典問題:
怎么在一次異步調(diào)用里return一個結(jié)果???

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

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

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

老司機建議試試【協(xié)程】,誰知對方想要合并兩個異步調(diào)用;

……

以上,是異步編程里要面對的一些難題,也是【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 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大。它由社區(qū)最早提出和實現(xiàn),ES6 將其寫進了語言標準,統(tǒng)一了用法,原生提供了Promise對象。

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

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

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

注意,為了行文方便,本章后面的resolved統(tǒng)一只指fulfilled狀態(tài),不包含rejected狀態(tài)。

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

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

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


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

為了解決這個問題,ES6提出了Promise的實現(xiàn)。

含義
Promise 對象用于一個異步操作的最終完成(或失?。┘捌浣Y(jié)果值的表示。
簡單點說,它就是用于處理異步操作的,異步處理成功了就執(zhí)行成功的操作,異步處理失敗了就捕獲錯誤或者停止后續(xù)操作。


例子

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

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

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

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

轉(zhuǎn)換實例如下:

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

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

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

下面代碼中,booksPromise和userPromise是兩個異步操作,只有等到它們的結(jié)果都返回了,才會觸發(fā)pickTopRecommentations這個回調(diào)函數(shù)。

const databasePromise = connectDatabase();

const booksPromise = databasePromise
  .then(findAllBooks);

const userPromise = databasePromise
  .then(getCurrentUser);

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


下面代碼中,如果 5 秒之內(nèi)fetch方法無法返回結(jié)果,變量p的狀態(tài)就會變?yōu)閞ejected,從而觸發(fā)catch方法指定的回調(diào)函數(shù)。

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);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容