Javascript 如果未解决,如何取消上次承诺?

Javascript 如果未解决,如何取消上次承诺?,javascript,promise,cancellation,Javascript,Promise,Cancellation,假设我有一个搜索函数来进行HTTP调用。 每个电话可能需要不同的时间。 所以我需要取消最后一个HTTP请求,只等待最后一个调用 async function search(timeout){ const data = await promise(timeout) return data; } // the promise function is only for visualizing an http call function promise(timeout){ retu

假设我有一个搜索函数来进行HTTP调用。 每个电话可能需要不同的时间。 所以我需要取消最后一个HTTP请求,只等待最后一个调用

async function search(timeout){

   const data = await promise(timeout)
   return data;

}
// the promise function is only for visualizing an http call
function promise(timeout){
   return new Promise(resolve,reject){
       setTimeout(function(){      
           resolve()
       },timeout) 
   }
}
search(200)
.then(function(){console.log('search1 resolved')})
.catch(function() {console.log('search1 rejected')})
search(2000)
.then(function(){console.log('search2 resolved')})
.catch(function(){console.log('search2 rejected')})
search(1000)
.then(function(){console.log('search3 resolved')})
.catch(function(){console.log('search3 rejected')})
需要查看“search1已解决”“search2已拒绝”“search3已解决”


如何实现此方案?

您可以定义一个工厂函数,用请求的取消行为封装您的
search()
方法。请注意,在这种情况下,有必要在
pending
集合中保留对每个
reject()
函数的引用,以实现提前取消

可取消功能(fn){
const pending=新集合();
返回函数(){
返回新承诺(异步(解析、拒绝)=>{
让我们安定下来;
让结果;
试一试{
待定。添加(拒绝);
解决=决心;
result=wait Promise.resolve(fn.apply(这个,参数));
}捕获(错误){
解决=拒绝;
结果=错误;
}
//如果本承诺未被取消
如果(待定,有(拒绝)){
//取消在此之前拨打的电话中的待定承诺
for(挂起的常量取消){
待定。删除(取消);
如果(取消!==拒绝){
取消();
}否则{
打破
}
}
解决(结果);
}
});
};
}
//内部API函数
函数searchImpl(超时){
返回新承诺((解决、拒绝)=>{
setTimeout(解析,超时);
});
}
//将内部API函数传递给Cancelable()
//并将返回值用作外部API函数
const search=可取消(searchImpl);
搜索(200)。然后(()=>{
console.log('search1已解决');
}, () => {
console.log(“search1被拒绝”);
});
搜索(2000)。然后(()=>{
console.log('search2已解决');
}, () => {
console.log(“search2被拒绝”);
});
搜索(1000)。然后(()=>{
console.log('search3已解决');
}, () => {
console.log(“search3被拒绝”);
});
搜索(500)。然后(函数(){
console.log('search4已解决');
}, () => {
console.log('search4已拒绝');

});承诺本身是不可取消的,但在有限的意义上会被取消,因为它会导致承诺被拒绝

考虑到这一点,可以通过少量的细化和希望取消的承诺返回功能来实现取消

function makeCancellable(fn) {
    var reject_; // cache for the latest `reject` executable
    return function(...params) {
        if(reject_) reject_(new Error('_cancelled_')); // If previous reject_ exists, cancel it.
                                                       // Note, this has an effect only if the previous race is still pending.
        let canceller = new Promise((resolve, reject) => { // create canceller promise
            reject_ = reject; // cache the canceller's `reject` executable
        });
        return Promise.race([canceller, fn.apply(null, params)]); // now race the promise of interest against the canceller
    }
}
假设您的http调用函数名为
httpRequest
promise
令人困惑):

现在,每次调用
search()


如有必要,catch回调可以测试err.message=='\u cancelled.
,以区分取消和其他拒绝原因。

与PatrickRoberts的答案类似,我建议使用
映射来维护未决承诺的列表

但是,我不会在promise构造函数之外维护对
reject
回调的引用。我建议放弃拒绝过时承诺的想法。相反,忽略它。将其包装在一个承诺中,该承诺永远不会解析或拒绝,但仍然是一个永远不会改变状态的死承诺对象。事实上,这种无声的承诺在你需要的每一个场合都是一样的

下面是它的样子:

constdelay=(超时,数据)=>newpromise(解析=>setTimeout(()=>resolve(数据),超时));
const godot=新承诺(()=>null);
const search=(函数(){//闭包。。。
const requests=new Map;//…以便共享变量
设id=1;
返回异步函数搜索(){
让持续时间=Math.floor(Math.random()*2000);
let request=delay(持续时间,“data”+id);//这将是一个HTTP请求
requests.set(request,id++);
让数据=等待请求;
如果(!requests.has(request))返回godot;//永不解析。。。
for(让[pendingRequest,pendingId]请求){
如果(pendingRequest==请求)中断;
请求。删除(待处理请求);
//只是为了演示,我们输出了一些东西。
//在真实场景中不需要:
log(“忽略搜索”+pendingId);
}
请求。删除(请求);
返回数据;
}    
})();
const reportSuccess=data=>console.log(“搜索已用“+数据解决”);
const reportError=err=>console.log('搜索被拒绝,带有'+err');
//定期生成搜索。
//在真实场景中,这可能是对关键事件的响应。
//每个承诺都会以随机的持续时间进行解析。

setInterval(()=>search().then(reportSuccess).catch(reportError),100如果您有多个承诺链接在一起-当其中一个承诺被拒绝时,链将停止。你没有把它们链接起来,所以它们每次都会执行。Aksi回答了这个问题,但你应该转向异步,等待它变得更简单。这不是我想要的场景。我只需要取消promise函数的最后一个调用如果尚未解决promise函数只需像API一样可视化异步调用我只希望最后一个承诺得到解决或任何其他在1000毫秒之前解决的承诺你的
承诺
构造应该是
新承诺((解决,拒绝)=>…)
,记录
promise2解决了什么问题?
?您的问题不明确。如果在收到新请求时确实取消了最后一个承诺,那么您的代码示例应该生成“search1拒绝”,而不是“search1已解决”,因为
search
的第二次调用应该将以前的承诺标识为未解决。但是,如果您想给ever promise首先解析的优先级,那么输出应该是“search1 resolved”,另外两个被拒绝。Pl
const search = makeCancellable(httpRequest);
// Search 1: straightforward - nothing to cancel - httpRequest(200) is called
search(200)
.then(function() { console.log('search1 resolved') })
.catch(function(err) { console.log('search3 rejected', err) });

// Search 2: search 1 is cancelled and its catch callback fires - httpRequest(2000) is called
search(2000)
.then(function() { console.log('search2 resolved') })
.catch(function(err) { console.log('search3 rejected', err) });

// Search 3: search 2 is cancelled and its catch callback fires - httpRequest(1000) is called
search(1000)
.then(function() { console.log('search3 resolved') })
.catch(function(err) { console.log('search3 rejected', err) });