常用!提前 reject promise 的 2 種場景,收藏等於學會

語言: CN / TW / HK

想一想,Promise 如何實現提前 reject?

講道理,我們回憶下就知道 Promise 的特性就是:不能中斷。

一旦執行,我們無法知道它具體執行到哪裡了,只知道在 pending,最後 resolve 或者 reject 才知道執行完畢。

image.png

但需要提前 reject的這種應用場景也確實是存在的。

比如:

1. 點選按鈕,發起請求,再點選另外一個按鈕,通過提前 reject Promise,不再依賴後續請求;

2. 用 Promise 封裝非同步請求,當超過 N 秒後還未執行完,提前 reject Promise ,執行後續操作;

這裡的取消請求,並不是撤回 XHR 請求,而是不再需要請求結果,直接執行後面的步驟;


p1

如何實現?

不急,先想想,同步的中斷 promise 的情況,它大概是這樣的:

``` function someAsyncFunction() { return new Promise(function(resolve, reject) { // 在這裡執行非同步操作 if (/ 某個條件成立 /) { // 如果條件成立,中斷 promise reject(new Error("The promise was interrupted")); } }); }

someAsyncFunction().catch(function(error) { // 處理 promise 中斷的回撥函式 console.error(error.message); }); ```

沒什麼毛病,如果某個條件成立,reject 錯誤資訊;

那麼,那對於第一個問題,就很好理解了:

  1. 點選按鈕,發起請求,再點選另外一個按鈕,通過中斷 Promise,取消請求;

實現步驟拆解:

  1. 為了方便測試,我們找一個可供線上測試的 API http://jsonplaceholder.typicode.com/posts GET 請求可以直接拿到返回報文;
  2. 不借助請求庫,就用原生 XHR;
  3. 為了加強模擬效果,我們再用一個 setTimeout 函式,延長成功返回的時間,意思是:請求至少要 10s+ 才會成功返回;
  4. 寫一個全域性的 cancelFn 方法,然後在 promise 內部重寫它,當呼叫時,會直接 reject ,便實現了中斷;

``` const baseURL='http://jsonplaceholder.typicode.com/posts';

let cancelFn=function(){}

function request(req){ return new Promise((resolve,reject)=>{ let xhr=new XMLHttpRequest(); xhr.open(req.method || 'GET',baseURL); xhr.onload=function(){ if(xhr.readyState==4 && (xhr.status>=200 && xhr.status<300)){ setTimeout(()=>{ resolve({data:JSON.parse(xhr.responseText)}) },10000) }else{ reject(xhr.status) } } xhr.onerror=function(){ reject('中斷 promise...') } xhr.send(req.data || null);

    cancelFn=function(msg){
        reject({message:msg})
    }
})

};

let send=document.querySelector('.send'); let cancel=document.querySelector('.cancel'); send.addEventListener('click',async function(){ console.log('正在請求中...') let {data}=await request({}) console.log(data) }); cancel.addEventListener('click',function(){ cancelFn('中斷 promise'); }) ```

可以在碼上掘金,開啟控制檯測測看。

http://code.juejin.cn/pen/7173900335335866407

p2

對於第 2 個問題:

  1. 用 Promise 封裝非同步請求,當超過 N 秒後還未執行完,中斷 Promise ,執行後續操作;

解決這個問題,用到一個巧思:

Promise.race:

一旦迭代器中的某個 promise 解決或拒絕,返回的 promise 就會解決或拒絕。

我們把手動執行的超時中斷,和業務邏輯的 prosmie 鏈條放在一起,超過 N 秒後,呼叫 cancelFn 方法,在 race 的 競爭策略 下,若 N 秒後請求還沒返回,則直接 reject 返回,則實現了中斷;

程式碼實現:

``` const baseURL='http://jsonplaceholder.typicode.com/posts';

let cancelFn=function(){}

let readUrlPromise=url=>{ return new Promise((resolve,reject)=>{ let xhr=new XMLHttpRequest(); xhr.open("GET",url); xhr.onreadystatechange=function(){ if(xhr.readyState==4 && xhr.status==200){ setTimeout(()=>{ resolve({data:JSON.parse(xhr.responseText)}) },3000) // 用 setTimeout 假設請求至少需要 3 s }else if(xhr.readyState==4 && xhr.status!=200){ reject('請求失敗'); } } xhr.onerror=function(){ reject('請求失敗'); } xhr.send(null); cancelFn=function(msg){ reject(msg); } }) }

let rest=function(N){ return Promise.race([ readUrlPromise(baseURL), uploadTimeout(N) ]).then(data=>{ console.log('url1'); console.log(data); }) }

function uploadTimeout(N){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ cancelFn('請求超時,中斷promise') },N*1000) }) }

rest(2) // 設定 2 s 後中斷 promise; ```

控制檯截圖:

image.png

如果 N < 請求響應時間,則不會觸發中斷攔截;

http://code.juejin.cn/pen/7174026521235963912

另外,要提一下的是,著名請求庫 axios。也有中斷請求的功能,同樣是利用 promise 實現一個競態限制,有興趣可自行研究;


OK,以上便是本篇分享,希望各位工友喜歡~ 歡迎點贊、收藏、評論 🤟

我是掘金安東尼 🤠 100 萬人氣前端技術博主 💥 INFP 寫作人格堅持 1000 日更文 ✍ 關注我,安東尼陪你一起度過漫長程式設計歲月 🌏

😹 加我微信 ATAR53,拉你入群,定期抽獎、粉絲福利多多。只學習交友、不推文賣課~

😸 我的公眾號:掘金安東尼,在上面,不止程式設計,更多還有生活感悟~

😺 我的 GithubPage: http://tuaran.github.io,它已經被維護 4 年+ 啦~


本文正在參加「金石計劃 . 瓜分6萬現金大獎」