Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/396.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Javascript承诺中取消超时?_Javascript_Promise_Settimeout_Cancellation - Fatal编程技术网

如何在Javascript承诺中取消超时?

如何在Javascript承诺中取消超时?,javascript,promise,settimeout,cancellation,Javascript,Promise,Settimeout,Cancellation,我在玩弄JavaScript中的承诺,并尝试使用setTimeout函数: function timeout(ms) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve('timeout done'); }, ms); }); } var myPromise=timeout(3000); myPromise.then(function(resu

我在玩弄JavaScript中的承诺,并尝试使用setTimeout函数:

function timeout(ms) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('timeout done');
    }, ms);
  }); 
}

var myPromise=timeout(3000); 

myPromise.then(function(result) { 
  console.log(result); // timeout done
})
相当直接,但我想知道我该如何在承诺解决之前取消我的超时
timeout
返回
Promise
对象,因此我失去了对
setTimeout
返回值的访问权限,无法通过
cleartimout
取消超时。最好的办法是什么


顺便说一句,这并没有真正的目的,我只是想知道这将如何处理。此外,我将其放在这里

编辑2021所有平台都将AbortController作为取消原语,并且有一些内置支持

在Node.js中
//从ESM中的'timers/promises'//导入{setTimeout}
const{setTimeout}=require('timers/promises');
const ac=新的中止控制器();
//可取消超时
(异步()=>{
等待设置超时(1000,null,{signal:ac.signal});
})();
//中止超时,以错误中止方式拒绝
ac.中止();
在浏览器中 您可以polyfill此API并使用与上述示例相同的方法:


函数延迟(毫秒,值,{signal}={}){
返回新承诺((解决、拒绝)=>{
常量侦听器=()=>{
清除超时(计时器);
拒绝(新错误(‘中止’);
};
常量计时器=设置超时(()=>{
信号?.removeEventListener('abort',listener);
决心(价值);
},ms);
如果(信号?。中止){
监听器();
}
信号?.addEventListener('abort',listener);
});
}

您可以从
timeout
函数返回一个取消器,并在需要时调用它。这样,您就不需要全局(或在外部作用域上)存储
timeoutid
,也可以管理对函数的多个调用。通过函数
timeout
返回的对象的每个实例都有自己的取消器,可以执行取消

function timeout(ms) {
  var timeout, promise;

  promise = new Promise(function(resolve, reject) {
    timeout = setTimeout(function() {
      resolve('timeout done');
    }, ms);
  }); 

  return {
           promise:promise, 
           cancel:function(){clearTimeout(timeout );} //return a canceller as well
         };
}

var timeOutObj =timeout(3000); 

timeOutObj.promise.then(function(result) { 
  console.log(result); // timeout done
});

//Cancel it.
timeOutObj.cancel();

然而,PSL的答案是正确的——这里有一些警告,我会做一些不同的事情

  • 清除超时意味着代码将无法运行-因此我们应该拒绝承诺
  • 在我们的例子中,返回两个东西是不必要的,我们可以在JavaScript中使用monkey补丁
在这里:

function timeout(ms, value) {
    var p = new Promise(function(resolve, reject) {
        p._timeout = setTimeout(function() {
            resolve(value);
        }, ms);
        p.cancel = function(err) {
            reject(err || new Error("Timeout"));
            clearTimeout(p._timeout); // We actually don't need to do this since we
                                      // rejected - but it's well mannered to do so
        };
    });
    return p;
}
这样我们就可以做到:

var p = timeout(1500)
p.then(function(){
     console.log("This will never log");
})

p.catch(function(){
     console.log("This will get logged so we can now handle timeouts!")
})
p.cancel(Error("Timed out"));
人们可能对全面取消感兴趣,事实上,一些库直接支持这一功能。事实上,我敢说大多数人都这么做了。但是,这会导致干扰问题。引用KrisKowal的话:

我对取消的立场有所变化。我现在确信取消(bg:传播)在承诺抽象中本质上是不可能的,因为承诺可以是多个依赖项,并且可以在任何时候引入依赖项。如果任何被抚养人取消一项承诺,它将能够干扰未来的被抚养人。有两种方法可以解决这个问题。一个是引入一个单独的取消“功能”,可能作为参数传递。另一个是引入一个新的抽象,一个可能是可定义的“任务”,作为交换,要求每个任务只有一个观察者(一个然后调用,永远),可以在不担心干扰的情况下取消。Tasks将支持fork()方法来创建新任务,允许另一个dependee保留该任务或推迟取消

function timeout(ms) {
  var timeout, promise;

  promise = new Promise(function(resolve, reject) {
    timeout = setTimeout(function() {
      resolve('timeout done');
    }, ms);
  }); 

  return {
           promise:promise, 
           cancel:function(){clearTimeout(timeout );} //return a canceller as well
         };
}

var timeOutObj =timeout(3000); 

timeOutObj.promise.then(function(result) { 
  console.log(result); // timeout done
});

//Cancel it.
timeOutObj.cancel();

以上是@Benjamin和@PSL的答案,但是如果您需要外部源使用可取消的超时,而内部被取消,该怎么办

例如,交互可能看起来有点像这样:

// externally usage of timeout 
async function() {
  await timeout() // timeout promise 
} 

// internal handling of timeout 
timeout.cancel() 
let timeout = createTimer(100)
我自己也需要这种实现,所以我想到了以下几点:

/**
 * Cancelable Timer hack.
 *
 *  @notes
 *    - Super() does not have `this` context so we have to create the timer
 *      via a factory function and use closures for the cancelation data.
 *    - Methods outside the consctutor do not persist with the extended
 *      promise object so we have to declare them via `this`.
 *  @constructor Timer
 */
function createTimer(duration) {
  let timerId, endTimer
  class Timer extends Promise {
    constructor(duration) {
      // Promise Construction
      super(resolve => {
        endTimer = resolve
        timerId = setTimeout(endTimer, duration)
      })
      // Timer Cancelation
      this.isCanceled = false
      this.cancel = function() {
        endTimer()
        clearTimeout(timerId)
        this.isCanceled = true
      }
    }
  }
  return new Timer(duration)
}
现在您可以像这样使用计时器:

// externally usage of timeout 
async function() {
  await timeout() // timeout promise 
} 

// internal handling of timeout 
timeout.cancel() 
let timeout = createTimer(100)
在其他地方取消承诺:

 if (typeof promise !== 'undefined' && typeof promise.cancel === 'function') {
  timeout.cancel() 
}

这是我用打字机写的答案:

  private sleep(ms) {
    let timerId, endTimer;
    class TimedPromise extends Promise<any> {
      isCanceled: boolean = false;
      cancel = () => {
        endTimer();
        clearTimeout(timerId);
        this.isCanceled = true;
      };
      constructor(fn) {
        super(fn);
      }
    }
    return new TimedPromise(resolve => {
      endTimer = resolve;
      timerId = setTimeout(endTimer, ms);
    });
  }

虽然我在任何地方都找不到它的文档,但是Promise的“委托人”函数似乎与
var p=new Promise()
在同一事件回合中运行,因此在委托人内部不能有对
p
的引用。这个解决方案(至少是我能想到的唯一一个)相当难看,但很有效。因此,这是一个更好的解决方案。作为引文的补充,克里斯科瓦尔提出了更多的想法@Benjamin Grunbaum有更好的答案。如果您向承诺中添加成员,则您将无法取消受抚养人承诺(即.then()结果);有关更多信息,请参阅它提供的
TypeError:无法在timeout函数中设置未定义的属性“\u timeout”。您也可以使用decorator,更多详细信息如下: