取消承诺链?

我链接了一系列的承诺:

this.getData.then(this.getMoreData).then(this.getEvenMoreData); 

在某些时候,用户可能决定取消该请求并请求其他内容。

如何取消链的传播?

你必须在每个链式方法中检查状态(是否应该取消):

 var userRequestedCancel = false; this .getData() .then(function() { if(userRequestedCancel) { return Promise.reject('user cancelled'); } return getMoreData(); }) .then(function() { if(userRequestedCancel) { return Promise.reject('user cancelled'); } return getEvenMoreData(); }) 

或者可能是稍微更优雅的方式(编辑以传递callback方法的上下文和参数)

 var currentReq = false; var userRequestedCancel = false; var shouldContinue = function(cb,args) { if(userRequestedCancel) { return Promise.reject('user cancelled'); } currentReq = cb.apply(this,args); return currentReq; } var onCancel = function() { userRequestedCancel = true; currentReq && currentReq.abort(); } this .getData() .then(function() { return shouldContinue(getMoreData,arguments); }) .then(function() { return shouldContinue(getEvenMoreData,arguments); }) 

如果您还需要取消当前请求,这很简单,将当前的ajax请求设置为全局变量,并且无论事件将userRequestedCancel标志设置为true,还要取消ajax请求(请参阅上面的编辑代码) )

来自jquery论坛 :

为了处理潜在的应用程序错误,我会一遍又一遍地执行以下操作:复制代码

 var deferred = $.Deferred().done( stuff_to_do ).fail( handle_application_error ); $.post( url, params, function(data) { special_error_check(data, deferred) }, 'json'); 

special_error_check()查看JSON数据是否包含错误消息。 如果是这样,它调用deferred.reject()。 如果没有错误,则调用deferred.resolve()。

要取消承诺链,您需要抛出错误。 只需看看下面的代码

 function CancelError() { this.message = 'Cancelled'; } obj .then(function() { throw new CancelError(); }) .catch(function(err) { if (err instanceof CancelError) { // Promise got cancelled } throw err; // throw the other mistakes }); 

有趣的小挑战!

如果不确切知道你正在启动哪个任务,请求或进程以及用户如何中断所述进程,很难推荐任何解决方案来“破解” .then(...)链而不做一些黑客/技巧触发.catch(...)拒绝回调。

话虽这么说,看看这个例子是否有所启发。

特别注意makeInterruptablePromise函数及其使用方法:

 var bar = $('.progress-bar'); var h3 = $("h3"); var isEscape; function log(msg, replace) { h3[replace ? 'html' : 'append'](msg + "
"); } $(document).keydown(e => { switch(e.keyCode) { case 27: //ESCAPE return isEscape = true; case 32: //SPACE return runDemo(); } }); function makeInterruptablePromise(cbStatus) { return new Promise((resolve, reject) => { function loop() { switch(cbStatus()) { case 1: return resolve(); case -1: return reject(); default: requestAnimationFrame(loop); } } //Don't forget to start the loop! loop(); }) } function runDemo() { log("Wait for it... (ESC to interrupt, SPACE to replay)", true); isEscape = false; var timeToComplete = 2000; var timeStart = Date.now(); function updateBar() { var timeDiff = Date.now() - timeStart; var timePercent = timeDiff / timeToComplete; TweenMax.set(bar, {scaleX: 1 - timePercent}); return timePercent > 1; } makeInterruptablePromise(() => { if(isEscape) return -1; if(updateBar()) return 1; return 0; }) .then(() => log("Inside *then* chain.")) .catch(() => log("Skipped *then* chain!")) } runDemo(); //Run first time.
 body { background-color: #123456; color: #fff; } .progress-bar { display: block; width: 200px; height: 10px; background-color: #88f; transform-origin: top left; }