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