promise-then 深刻理解異步

異步經常使用的幾種方式

setTimeout/EventEmitter/Promise/generator/async-await 今天着重來說解一下 promise數組

setTimeout和Promise隊列有何關聯

setTimeout(function(){console.log(4)},0); 
new Promise(function(resolve){ 
    console.log(1) 
    for( var i=0 ; i<10000 ; i++ ){ i==9999 && resolve() } 
    console.log(2) })
.then(function(){ console.log(5) });
 console.log(3);

在這裏咱們須要先理解We have a problem with promises

doSomething().then(function () {
      return doSomethingElse();
    });
    
    doSomething().then(function () {
      doSomethingElse();
    });
    
    doSomething().then(doSomethingElse());
    
    doSomething().then(doSomethingElse);

promise 在應用的過程當中須要注意的地方

1.new promise (resolve,reject){};必定要resolve,或者reject 出去不要,作反作用函數。
2.then 操做裏面 接收 promise 對象,若是在promise裏面是執行的純函數,也能夠返回一個 promise。resolve("常量")。避免promise 穿透

正確使用 promise 金字塔代碼

新手錯誤 #1: promise版的金字塔問題
觀察你們如何使用 PouchDB 這類大型的 promise 風格的API,我發現大量錯誤的 promise 使用形式。最多見的錯誤就是下面這個:promise

remotedb.allDocs({
  include_docs: true,
  attachments: true
}).then(function (result) {
  var docs = result.rows;
  docs.forEach(function(element) {
    localdb.put(element.doc).then(function(response) {
      alert("Pulled doc with id " + element.doc._id + " and added to local db.");
    }).catch(function (err) {
      if (err.status == 409) {
        localdb.get(element.doc._id).then(function (resp) {
          localdb.remove(resp._id, resp._rev).then(function (resp) {

// et cetera...
是的,實際上你能夠像使用回調同樣使用 promises,恩,就像用打磨機去削腳趾甲同樣,你確實能夠這麼作。緩存

而且若是你覺得這樣的錯誤只限於初學者,那麼你會驚訝於我其實是在黑莓官方開發者博客上看到上面的代碼。老的回調風格的習慣難以消滅。(至開發者: 抱歉選了你的例子,可是你的例子將會有積極的教育意義)異步

正確的風格應該是這樣:async

remotedb.allDocs(...).then(function (resultOfAllDocs) {
  return localdb.put(...);
}).then(function (resultOfPut) {
  return localdb.get(...);
}).then(function (resultOfGet) {
  return localdb.put(...);
}).catch(function (err) {
  console.log(err);
});

這種寫法被稱爲 composing promises ,是 promises 的強大能力之一。每個函數只會在前一個 promise 被調用而且完成回調後調用,而且這個函數會被前一個 promise 的輸出調用,稍後咱們在這塊作更多的討論。ide

  • 4.promise 中foreach 操做,轉換成 promise.all()=>返回一個數組
  • 5.在任何應用到了promise的過程當中,必定要注意使用catch 將異常拋出來,否則會堵塞整個加載

使用反作用調用而非返回

下面的代碼有什麼問題?函數

somePromise().then(function () {
  someOtherPromise();
}).then(function () {
  // Gee, I hope someOtherPromise() has resolved!
  // Spoiler alert: it hasn't.
});

好了,如今是時候討論一下關於 promises 你所須要知道的一切。code

認真的說,這是一個一旦你理解了它,就會避免全部我說起的錯誤的古怪的技巧。你準備好了麼?對象

就如我前面所說,promises 的奇妙在於給予咱們之前的 return 與 throw。可是在實踐中這究竟是怎麼一回事呢?blog

每個 promise 都會提供給你一個 then() 函數 (或是 catch(),實際上只是 then(null, ...) 的語法糖)。當咱們在 then() 函數內部時:

somePromise().then(function () {
  // I'm inside a then() function!
});

咱們能夠作什麼呢?有三種事情:

return 另外一個 promise
return 一個同步的值 (或者 undefined)
throw 一個同步異常
就是這樣。一旦你理解了這個技巧,你就理解了 promises。所以讓咱們逐個瞭解下。

返回另外一個 promise
這是一個在 promise 文檔中常見的使用模式,也就是咱們在上文中提到的 「composing promises」:

getUserByName('nolan').then(function (user) {
  return getUserAccountById(user.id);
}).then(function (userAccount) {
  // I got a user account!
});

注意到我是 return 第二個 promise,這個 return 很是重要。若是我沒有寫 returngetUserAccountById() 就會成爲一個反作用,而且下一個函數將會接收到 undefined 而非 userAccount
返回一個同步值 (或者 undefined)
返回 undefined 一般是錯誤的,可是返回一個同步值其實是將同步代碼包裹爲 promise 風格代碼的一種很是讚的手段。舉例來講,咱們對 users 信息有一個內存緩存。咱們能夠這樣作:

getUserByName('nolan').then(function (user) {
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];    // returning a synchronous value!
  }
  return getUserAccountById(user.id); // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
});

是否是很贊?第二個函數不須要關心 userAccount 是從同步方法仍是異步方法中獲取的,而且第一個函數能夠很是自由的返回一個同步或者異步值。

不幸的是,有一個不便的現實是在 JavaScript 中無返回值函數在技術上是返回 undefined,這就意味着當你本意是返回某些值時,你很容易會不經意間引入反作用。

出於這個緣由,我我的養成了在 then() 函數內部 永遠返回或拋出 的習慣。我建議你也這樣作。

拋出同步異常
談到 throw,這是讓 promises 更加讚的一點。好比咱們但願在用戶已經登出時,拋出一個同步異常。這會很是簡單:

getUserByName('nolan').then(function (user) {
  if (user.isLoggedOut()) {
    throw new Error('user logged out!'); // throwing a synchronous error!
  }
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];       // returning a synchronous value!
  }
  return getUserAccountById(user.id);    // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
}).catch(function (err) {
  // Boo, I got an error!
});

若是用戶已經登出,咱們的 catch() 會接收到一個同步異常,而且若是 後續的 promise 中出現異步異常,他也會接收到。再強調一次,這個函數並不須要關心這個異常是同步仍是異步返回的。

這種特性很是有用,所以它可以在開發過程當中幫助定位代碼問題。舉例來講,若是在 then() 函數內部中的任何地方,咱們執行 JSON.parse(),若是 JSON 格式是錯誤的,那麼它就會拋出一個異常。若是是使用回調風格,這個錯誤極可能就會被吃掉,可是使用 promises,咱們能夠輕易的在 catch() 函數中處理它了。

必定要注意咱們在then 函數內部的時候

咱們能夠作什麼呢?有三種事情:

return 另外一個 promise
return 一個同步的值 (或者 undefined)
throw 一個同步異常

參考資料

相關文章
相關標籤/搜索