如何取消一個正在執(zhí)行的Promise

ajax(XMLHttpReqeust)終止請求的方式:XMLHttpRequest.abort()

如果該請求已被發(fā)出,XMLHttpRequest.abort() 方法將終止該請求。當(dāng)一個請求被終止,它的 readyState 將被置為 XMLHttpRequest.UNSENT (0),并且請求的 status 置為 0

var xhr = new XMLHttpRequest(),
    method = "GET",
    url = "https://developer.mozilla.org/";
xhr.open(method, url, true);

xhr.send();

if (OH_NOES_WE_NEED_TO_CANCEL_RIGHT_NOW_OR_ELSE) {
  xhr.abort();
}
var request;
if (request) {
  request.abort();
}
request = $.ajax({
  // 各種參數(shù)
})

取消promise

方案一 借助Promise.reject
function getPromise (callback) {
  let _resolve, _reject;
  const promise = new Promise((res, rej) => {
    _resolve = res;
     _reject = rej;
    callback && callback(res, rej);
  })
  return {
      promise,
      abort: () => {
        _reject({ message: "promise aborted" })
      }
    }
}

function runCallback (resolve, reject) {
    setTimeout(() => {
        resolve(12345);
    }, 5000)
}
const { promise, abort } = getPromise(runCallback);
promise.then(/*...*/).catch(/*...*/)
abort()
方案二 借助Promise.race

Promise.race(iterable) 方法返回一個 promise,一旦迭代器中的某個promise解決或拒絕,返回的 promise就會解決或拒絕

function getPromiseWithAbort (p) {
  let obj = {};
  let p1 = new Promise((resolve, reject) => {
    obj.abort = reject;
  });
  obj.promise = Promise.race([p, p1]);
  return obj
}
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(12345);
    }, 5000)
});
const promiseObj = getPromiseWithAbort(promise)
promiseObj.promise.then(/*...*/).catch(/*...*/)
obj.abort('取消執(zhí)行')

Axios中取消請求的實現(xiàn)

Axios :主流的、基于 XMLHttpReqeust、promise 的 HTTP 庫如何實現(xiàn) -- cancelToken

// 創(chuàng)建取消令牌的生成器對象
const CancelToken = axios.CancelToken;
// 獲取令牌對象
const source = CancelToke.source();
axios.get('/url/123', {
  cancelToken: source.token
});
// 2秒后取消請求
setTimeout(() => {
  source.cancel();
}, 2000);
實現(xiàn)思路

想要實現(xiàn)取消某個請求,我們需要為該請求配置一個 cancelToken ,然后在外部調(diào)用一個 cancel 方法。
請求的發(fā)送是一個異步過程,最終會執(zhí)行 xhr.send 方法,xhr 對象提供了 abort 方法,可以把請求取消。因為我們在外部是碰不到 xhr 對象的,所以我們想在執(zhí)行 cancel 的時候,去執(zhí)行 xhr.abort 方法。
我們在 xhr 異步請求的過程中,插入一段代碼,當(dāng)我們在外部執(zhí)行 cancel 函數(shù)的時候,會驅(qū)動這段代碼的執(zhí)行,然后執(zhí)行 xhr.abort 方法取消請求。
取消請求必然是個異步的操作,自然想到用 Promise 實現(xiàn)異步,也就是在 cancelToken 中保存一個 pending 狀態(tài)的 Promise 對象,然后當(dāng)我們執(zhí)行 cancel方法的時候,能夠訪問這個 Promise 對象,把它從 pending 狀態(tài)變成 resolved 狀態(tài),這樣我們就可以在 then 函數(shù)中去實現(xiàn)取消請求的邏輯。

// 創(chuàng)建Promise,返回開關(guān)cancel
function source () {
  let cancel;
  const promise = new Promise((resolve) => {
    cancel = resolve;
  }
  return {
    token: promise  // pending狀態(tài)的promise
    cancel,      // 使上面的promise變成resolve狀態(tài)
  }
}
// 發(fā)請求
function axios_get (config) {
    const xhr = new XMLHttpRequest(),
    method = "GET",
    url = "https://developer.mozilla.org/";
    xhr.open(method, url, true);
    xhr.send();
    if (config.cancelToken) {
      config.cancelToken.then(() => {
          xhr.abort();
      })
    }
}
// 代碼執(zhí)行
const source = source();
axios_get({ cancelToken: source.token });
setTimeout(() => {
  source.cancel();
}, 5000)

AbortSignal

AbortSignal是什么

AbortSignal 是一個實驗性的API,可以實例化一個信號對象(signal object)
AbortController 可以實例化一個信號對象的控制器
AbortController 就像遙控器一樣,可以控制中斷的信號
AbortSignal的出現(xiàn)使promise從語義上變?yōu)榭芍兄沟摹2⑶?,只要符合?guī)范,所有異步操作都能變?yōu)椤缚芍兄沟摹?br> 一個栗子

// 初始化控制器
const controller = new AbortController();
const signal = controller.signal;

// 監(jiān)聽abort事件
signal.addEventListener('abort', () => {
  console.log("中斷信號發(fā)出");
})

// 控制器發(fā)出中斷信號
controller.abort()
console.log("是否中斷:", signal.aborted)
// 依次打印  中斷信號發(fā)出、 是否中斷:true
AbortSignal在fetch中的應(yīng)用

fetch API 中已經(jīng)集成了AbortSignal,使用時需要將controller中的信號對象作為signal參數(shù)傳遞給fetch,當(dāng)調(diào)用controller.abort()后,fetch的promise會變?yōu)?strong>AbortError DOMException reject

const controller = new AbortController()
fetch('xxx', {
  signal: controller.signal
}).then(() => {
}).catch(err => {
  if (err.name === '') {
  // 中止信號
  } else {
  // 其他錯誤
  }
})

Abort APi example (mdn.github.io)

AbortSignal與任何異步操作結(jié)合

不僅適用于fetch,任何異步操作只要符合如下規(guī)范,都可以與Abort Error集成

  1. AbortSignal 作為API的signal參數(shù)傳入
  2. 約定如果API返回的promise變?yōu)?strong>Abort Error DOMException reject則代表操作被中止
  3. 如果signal.aborted === true 則立刻讓promise變?yōu)閞eject
  4. 觀測AbortSignal狀態(tài)的變化
    DOM Standard (whatwg.org)
瀏覽器兼容性
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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