Javascript 承诺-是否可以强制取消承诺

Javascript 承诺-是否可以强制取消承诺,javascript,promise,cancellation,Javascript,Promise,Cancellation,我使用ES6承诺来管理我的所有网络数据检索,在某些情况下,我需要强制取消它们 基本上,这个场景是这样的,我在UI上有一个提前输入的搜索,请求被委托给后端,必须根据部分输入执行搜索。虽然此网络请求(#1)可能需要一点时间,但用户继续键入,这最终会触发另一个后端调用(#2) 这里#2自然优先于#1,因此我想取消承诺包装请求#1。我已经在数据层中缓存了所有承诺,因此理论上我可以在尝试提交#2承诺时检索到它 但是,一旦我从缓存中检索到承诺1,我如何取消它 有人能推荐一种方法吗?我查阅了Mozilla J

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

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

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

但是,一旦我从缓存中检索到承诺1,我如何取消它


有人能推荐一种方法吗?

我查阅了Mozilla JS参考资料,发现:

让我们来看看:

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
  console.log(value); // "two"
  // Both resolve, but p2 is faster
});
我们这里有p1和p2作为参数放入了
Promise.race(…)
,这实际上是在创建新的解析承诺,这是您所需要的。

不,我们还不能这样做。 ES6承诺还不支持取消。它正在开发中,它的设计是很多人非常努力的工作。声音消除语义很难正确理解,这是正在进行的工作。关于“取回”回购、ESDiscussion和其他几个关于GH的回购,存在着有趣的争论,但如果我是你,我会耐心等待

但是,但是,但是。。取消真的很重要! 事实上,在客户端编程中,取消确实是一个重要的场景。您描述的中止web请求之类的情况非常重要,而且无处不在

所以那语言把我搞砸了! 是的,很抱歉。承诺必须在进一步的事情被指定之前首先进入-因此他们没有使用一些有用的东西,比如
。最后
。取消
-但是它正在通过DOM进入规范。取消并不是事后诸葛亮,它只是一个时间限制,是API设计的一种更为迭代的方法

那我该怎么办? 您有几种选择:

  • 使用第三方库,比如who,它的移动速度比规范快得多,因此可以取消,也可以使用其他一些好东西——这就是像WhatsApp这样的大公司所做的
  • 传递取消令牌
使用第三方库是显而易见的。对于令牌,您可以让您的方法在中获取一个函数,然后调用它,如下所示:

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();
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
});
您的实际用例-
last
这对于令牌方法来说并不难:

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 token = {};
var promise = getWithCancel("/someUrl", token);

// later we want to abort the promise:
token.cancel();
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这样的库在这里并不“闪耀”,因为它们是可观察的库,它们只是具有用户级承诺库不受规范约束的相同优势。我想我们会等着在ES2016中看到观察到的东西何时会变成本地的。不过,它们很适合提前打印。

关于可撤销承诺的标准提案已经失败

承诺不是实现它的异步操作的控制面;混淆了所有者和消费者。相反,创建异步函数,这些函数可以通过一些传入的令牌来取消

另一个承诺是一个很好的代币,使得取消很容易通过
承诺来实现。race

示例:使用
Promise.race
取消前一个链的效果:

let cancel=()=>{};
input.oninput=功能(ev){
设项=ev.target.value;
log(`search for“${term}``);
取消();
让p=新承诺(resolve=>cancel=resolve);
race([p,getSearchResults(term)])。然后(results=>{
如果(结果){
log(`results for“${term}``,results);
}
});
}
函数getSearchResults(术语){
返回新承诺(解决=>{
让timeout=100+Math.floor(Math.random()*1900);
setTimeout(()=>resolve([term.toLowerCase(),term.toUpperCase()),timeout);
});
}

搜索:
我最近遇到了类似的问题

我有一个基于promise的客户端(不是网络客户端),我希望始终将最新请求的数据提供给用户,以保持UI的流畅

在与取消想法斗争之后,
Promise.race(…)
Promise.all(…)
我刚开始记住我的上一个请求id,当承诺兑现时,我只在数据与上一个请求的id匹配时才呈现数据


希望它能帮助别人。

对于Node.js和Electron,我强烈建议使用。它的作者是关键的打字脚本工程师之一,也是当前TC39提案的幕后推手。该库有很好的文档记录,Prex的一些内容很有可能符合标准

就个人而言,我来自C#背景,我非常喜欢Prex是基于现有框架建模的事实,即基于
CancellationTokenSource
/
CancellationToken
.NET API所采取的方法。根据我的经验,在托管应用程序中实现健壮的取消逻辑非常方便

我还通过使用绑定Prex来验证它在浏览器中是否工作

下面是一个取消延迟的示例(并用于其
取消令牌
延迟
):

请注意,取消是一场竞赛。也就是说,承诺可能已成功解决,但当您遵守承诺时(使用
wait
then
),取消也可能已触发。这取决于您如何处理这场比赛,但像我上面所做的那样,额外调用
令牌.throwIfCancellationRequested()
也无妨。

请参见

$npm安装承诺中止表

因为@jib拒绝我的修改,所以我在这里发布我的答案。这只是一些注释和使用更容易理解的变量名的方式
let controller = new AbortController();

let task = new Promise((resolve, reject) => {
  // some logic ...
  controller.signal.addEventListener('abort', () => reject('oops'));
});

controller.abort(); // task is now in rejected state
let controller = new AbortController();
fetch(url, {
  signal: controller.signal
});
let controller = new AbortController();
fetch(url, controller);