javascript - promise - 是否可以强制取消 promise

标签 javascript promise cancellation

我使用 ES6 Promises 来管理我所有的网络数据检索,在某些情况下我需要强制取消它们。

基本上情况是这样的,我在 UI 上有一个预先输入的搜索,其中请求委托(delegate)给后端必须根据部分输入执行搜索。虽然此网络请求 (#1) 可能需要一点时间,但用户继续键入最终触发另一个后端调用 (#2)

这里 #2 自然优先于 #1,所以我想取消 Promise 包装请求 #1。我已经在数据层中缓存了所有 Promise,因此理论上我可以在尝试为 #2 提交 Promise 时检索它。

但是,一旦我从缓存中检索到 Promise #1,我该如何取消它呢?

谁能推荐一种方法?

最佳答案

在现代 JavaScript 中——没有

promise 已经解决(哈),似乎永远不可能取消(待定的) promise 。

相反,有一个跨平台(节点、浏览器等)取消原语作为 WHATWG(也构建 HTML 的标准机构)的一部分,称为 AbortController。您可以使用它来取消返回 promise 而不是 promise 本身的函数:

// Take a signal parameter in the function that needs cancellation
async function somethingIWantToCancel({ signal } = {}) {
  // either pass it directly to APIs that support it
  // (fetch and most Node APIs do)
  const response = await fetch('.../', { signal });
  // return response.json;

  // or if the API does not already support it -
  // manually adapt your code to support signals:
  const onAbort = (e) => {
    // run any code relating to aborting here
  };
  signal.addEventListener('abort', onAbort, { once: true });
  // and be sure to clean it up when the action you are performing
  // is finished to avoid a leak
  // ... sometime later ...
  signal.removeEventListener('abort', onAbort);
}

// Usage
const ac = new AbortController();
setTimeout(() => ac.abort(), 1000); // give it a 1s timeout
try {
  await somethingIWantToCancel({ signal: ac.signal });
} catch (e) {
  if (e.name === 'AbortError') {
    // deal with cancellation in caller, or ignore
  } else {
    throw e; // don't swallow errors :)
  }
}

没有。我们还不能那样做。

ES6 promises 目前不支持取消。它正在路上,它的设计是很多人非常努力的事情。 声音取消语义很难正确处理,这项工作正在进行中。关于“fetch”存储库、esdiscuss 和 GH 上的其他几个存储库存在有趣的争论,但如果我是你,我会耐心等待。

但是,但是,但是..取消真的很重要!

事实上,取消确实是客户端编程中的一个重要场景。您描述的像中止网络请求这样的情况很重要,而且无处不在。

所以...语言把我搞砸了!

是的,对此感到抱歉。 Promises 必须在进一步的事情被指定之前首先进入 - 所以他们进入时没有一些有用的东西,比如 .finally.cancel - 不过它正在按照规范进行通过 DOM。取消不是事后的想法,它只是一个时间限制和一种更具迭代性的 API 设计方法。

那我能做什么呢?

您有多种选择:

  • 使用第三方库,例如 bluebird谁可以比规范更快地移动,因此可以取消以及其他一些好东西 - 这就是像 WhatsApp 这样的大公司所做的。
  • 传递一个取消 token

使用第三方库是很明显的。至于 token ,您可以让您的方法接受一个函数,然后调用它,如下所示:

function getWithCancel(url, token) { // the token is for cancellation
   var xhr = new XMLHttpRequest;
   xhr.open("GET", url);
   return new Promise(function(resolve, reject) {
      xhr.onload = function() { resolve(xhr.responseText); });
      token.cancel = function() {  // SPECIFY CANCELLATION
          xhr.abort(); // abort request
          reject(new Error("Cancelled")); // reject the promise
      };
      xhr.onerror = reject;
   });
};

哪个会让你做:

var token = {};
var promise = getWithCancel("/someUrl", token);

// later we want to abort the promise:
token.cancel();

您的实际用例 - last

使用 token 方法这并不难:

function last(fn) {
    var lastToken = { cancel: function(){} }; // start with no op
    return function() {
        lastToken.cancel();
        var args = Array.prototype.slice.call(arguments);
        args.push(lastToken);
        return fn.apply(this, args);
    };
}

哪个会让你做:

var synced = last(getWithCancel);
synced("/url1?q=a"); // this will get canceled 
synced("/url1?q=ab"); // this will get canceled too
synced("/url1?q=abc");  // this will get canceled too
synced("/url1?q=abcd").then(function() {
    // only this will run
});

不,像 Bacon 和 Rx 这样的库在这里并不“闪耀”,因为它们是可观察的库,它们只是具有与用户级 promise 库相同的优势,即不受规范约束。我想我们会等着看在 ES2016 中 Observables 成为原生的。不过,它们很适合提前输入。

关于javascript - promise - 是否可以强制取消 promise ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30233302/

相关文章:

javascript - 如何在 Promise 中的异步函数中断言?

javascript - 如何在 Javascript 中使用 Q 顺序运行 promise ?

Android 电子市场,订单取消原因 - 您取消了此订单

iOS可取消后台任务实现

javascript - 在 JavaScript 中写入 JSON 文件

javascript - Jq Bootstrap 多选 - 必须至少选择一个值

javascript - 使用模板将 https 链接替换为 <a>

javascript - Promise 应该被错误或字符串拒绝吗?

c# - 在没有 Task.Wait() 的情况下使用 cancellationToken

javascript - 马尔兹帕诺 + ReactJS?