JS promise用法總結

0、前言

????????處在前后端分離開發模式的時代,前端向后端請求數據似乎已經司空見慣,在稍微復雜一點的業務中就可能遇到串行接口的情況,這就會產生回調嵌套,回調過深會導致代碼可維護性差,因此需要一種解決回調過深的方案:promise。ES6 promise語法精深,本文只介紹工作中最常用到的promise相關知識。

1、promise語法

  • 創建promise
var promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 異步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
//實例
function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    var image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}

創建promise最重要的操作就是確定何時執行resolve方法

  • Promise.prototype.then方法
  1. 雖然then方法接受兩個函數作為參數,但一般只寫成功回調,并用catch代替失敗回調
//bad
promise.then(successFn,errorFn)
//good
promise.then(successFn).catch(errorFn)
  1. 根據successFn返回的結果,then有不同的返回值,then方法總是返回一個新的promise:
    2.1 當successFn返回普通值v1時,then返回原來的promise,并將v1作為后續then方法的成功回調參數;
    2.2 當successFn返回已經被resolved的promise時,then返回原來的promise,并將successFn返回的promise值作為后續then方法的成功回調參數;
    2.3 當successFn返回一個未被resolved的promise1時,then方法返回的promise將與promise1具有一致的狀態,可以看成then方法返回了promise1
  • Promise.prototype.resolve方法
    根據resolve方法接受的參數類型分為:
    1、promise,直接返回這個promise
    2、普通類型(不包含then方法的對象),返回一個立即resolve的promise
    3、不傳參數,返回一個立即resolve的promise
  • Promise.prototype.all方法
    接受promise組成的數組,并能使結果有序返回。缺點是一旦其中一個promise被rejected,所有數據都拿不到了。返回的結果以數組形式存儲,可按下標依次取出

2、promise運用場景

1、單個請求promise化

//異步加載圖片
function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    var image = new Image();
    image.onload = function() {
      resolve(image);
    };
    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };
    image.src = url;
  });
}
loadImageAsync(url).then(successFn).catch(handleError)

2、多個串行請求promise化
場景:先拿用戶信息,再獲取數據

//方案1
getUserInfo().then(getData).then(successFn).catch(handleError)
方案2
async function fn(){
    let userInfo=await getUserInfo();
    let data=await getData(userInfo);
    return data;
}
fn().then(successFn).catch(errorFn);

方案2的async函數寫法使得串行的鏈式調用變成了同步寫法,語義更加清楚
3、多個并行請求promise化
場景:一次性拿不同tab下的數據
方案1:使用Promise.all()方法

let tab1_promise=getHotData();
let tab2_promise=getLatestData();
let arr=[tab1_promsie,tab2_promise];
Promise.all(arr).then(([tab1_data,tab2_data])=>handleFn).catch(errorFn);

這種方式顯然有風險,可考慮如下方案2
方案2:順序執行promise

let arr=[tab1_promise,tab2_promise];
arr.reduce((task,promise)=>{
      return task.then(()=>return promise).then(successFn);
},Promise.resolve());

這種方案可以對每個promise的返回值單獨處理,不必等到所有數據一起返回才處理,更加靈活

3、總結

  • 創建一個promise的關鍵在于確定resolve的執行時機
  • then方法總是返回一個新的promise
  • 使用這種寫法promise.then(successFn).catch(errorFn),而不是promise.then(successFn,errorFn)
  • Promise.reslove()可以創造一個立即resolve的promise,結合reduce方法可使代碼更加精煉
  • async函數基于Promise,可解決串行鏈式調用過長的問題,并且語義清楚,推薦使用
  • Promise.all方法有風險,盡量不用

4、超時和可取消的Promise

// 可取消的Promise(本質上并沒有取消請求,只是不用promise的返回值而已)
export default function makeCancelable(promise){
    let hasCanceled_ = false;
    const wrappedPromise = new Promise((resolve, reject) => {
        promise.then((val) =>
            hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
        );
        promise.catch((error) =>
            hasCanceled_ ? reject({isCanceled: true}) : reject(error)
        );
    });
    return {
        promise: wrappedPromise,
        cancel() {
            hasCanceled_ = true;
        },
    };
}

// 超時的Promise
export const makeTimeoutPromise = ({ timeout = 300000, callback,promise }) => {
  // 默認5分鐘超時
  let hasTimeout = false;
  let timer = setTimeout(() => {
    hasTimeout = true;
  }, timeout);
  const wrappedPromise = new Promise((resolve, reject) => {
        promise.then((val) =>
            hasTimeout ? reject({hasTimeout: true}) : resolve(val)
        ).catch((error) =>
            hasTimeout ? reject({hasTimeout: true}) : reject(error)
        ).finally(()=>timer && clearTimeout(timer))
  }
  return wrappedPromise;
};

掌握以上這些,應該能夠應對工作中JS的異步處理了
https://mp.weixin.qq.com/s/cN40pHBfttZ3O2oEbVPAcg

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容