談談 Promise 以及實現 Fetch 的思路

Promise 是異步編程的一種解決方案。

Promise


/**
 * 屬性
 */
Promise.length
Promise.prototype

/**
 * 方法
 */
Promise.all(iterable)   // 所有成功觸發成功  任何失敗觸發失敗
Promise.race(iterable)  // 任意一個成功或失敗后觸發
Promise.reject(reason)
Promise.resolve(value)

/**
 * 原型
 */
Promise.prototype.constructor
//方法
Promise.prototype.catch(onRejected)
Promise.prototype.then(onFulfilled, onRejected)
Promise.prototype.finally(onFinally)

Promise 有三種狀態

  • pending: 初始狀態,既不是成功,也不是失敗狀態。
  • resolve: 意味着操作成功完成。(resoloved)
  • reject: 意味着操作失敗。

pending

pending 是初始狀態,執行 resolve/reject 會進入對應狀態,如果不執行,責一直為 pending 狀態

例如下面代碼,promise 將一直在 pending 狀態,不會執行 then/catch.

new Promise(function (resolve, reject) { })
  .then(res => console.log(res))
  .catch(err => console.log(err))

resolve

resolve 意味着操作成功完成, 如果有 .then,值會傳入 .then 的第一個參數函數里。

new Promise(function (resolve, reject) {
  resolve(1)
})
  .then(res => console.log(res))

then 的第一個參數是成功的回調,第一個參數的返回值會影響接下來鏈的去向。第一個參數的返回值一般有三種情況

  • 無返回值:會去執行下一個 .then ,沒有參數
  • 返回值非promise:調用下一個then的函數,參數為返回值
  • 返回值為promise:根據promise的執行結果,執行 下一個then/catch,如果一直是pending,則不執行下一個then/catch

例如想要在當前 then 終止,可以這樣操作:

  .then((res) => new Promise(() => {}))

reject

reject 意味着操作失敗。

使用 .catch 會捕獲到錯誤信息。

與代碼報錯(如 undefined.a)不同的是, 代碼報錯如果不使用 catch 捕獲,會向外傳遞,最終傳遞到根結點;而 reject 屬於 promise 錯誤,即使不使用 catch 捕獲也不會對全局有影響。

用 promise 實現 fetch

先來看幾個問題:

  1. 如果請求 code 404, 會走 then 還是 catch? (答案:then)
  2. 控制台能看到一行 404 的錯誤, 為什麼還是走 then 不是 catch 呢
  3. 如果請求跨域失敗,走 then 還是 catch?(答案:catch)
  4. 同樣是控制台看到錯誤,兩者有什麼區別呢?
  5. 跨域失敗的報錯, 和 then 中 undefined.a 報錯,如果都不 catch,後者在 react 腳手架開發環境頁面會蹦,兩者有什麼區別?

帶着這幾個問題,來看看 fetch。

fetch 返回值是 promise,所以有三種狀態 pending、resolve、reject.

  • pending: 請求中
  • resolve: 請求成功(code 200/404/500 等, 非 200 控制台輸出錯誤)
  • reject: 請求失敗(跨域失敗、連接超時、無網絡等,控制台輸出錯誤)

我們還發現,請求失敗時,只能 catch 到最後一行錯誤, 如圖

捕獲后

為什麼 404 在控制台看到錯誤,還走 then, resolve 如何實現

實現有幾個難點,

  1. throw 後面代碼不會執行;
  2. 先報錯,后執行 then;
  3. catch 后錯誤不會打印在控制台;

試了下,Promise.reject(‘xxx’) 這樣的報錯方式雖然是微觀任務,但是總是在.then之後才在控制台輸出,更像是宏觀任務。所以也加個setTImeout宏觀任務調至後面。

var fetch = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      if ('請求成功 200') {
        resolve('Response數據結構');
      } else if ('請求成功 404,500等') {
        Promise.reject('GET xxxxxxxx 404');
        setTimeout(function () {
          resolve('Response數據結構');
        });
      }
    })
  })
}

請求失敗 例如跨域失敗 reject 如何實現呢

同樣加個 setTimeout

var fetch = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      if ('請求成功 200') {
        resolve('Response數據結構');
      } else if ('請求成功 404,500等') {
        Promise.reject('GET xxxxxxxx 404');
        setTimeout(function () {
          resolve('Response數據結構');
        });
      } else if ('請求失敗') {
        Promise.reject('Access to fetch xxxxx with CORS disabled.');
        Promise.reject('GET xxxxx net::ERR_FAILED');
        setTimeout(function () {
          reject('TypeError: Failed to fetch');
        });
      }
    })
  })
}

還是有些問題,我們實現的因為在promise 中,錯誤會有前綴 Uncaught (in promise)。瀏覽器客戶端應該有更好的實現方式。

最後總結一下 fetch 的三種情況

  • pending: 請求中
  • resolve: 請求成功(code 200: 調用 resolve 返回數據; code: 404/500 等, 先拋錯,再調用 resolve 返回數據。)
  • reject: 請求失敗(跨域失敗、連接超時、無網絡等,先控制台拋錯,再調用 reject)

拋錯均不影響代碼執行,與 undefined.a 不同。

whosmeya.com

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準