如何停止JavaScript中的异步函数?

如何停止JavaScript中的异步函数?,javascript,asynchronous,settimeout,Javascript,Asynchronous,Settimeout,我有一些异步问题。我正在研究一个物体。这是一个计时器,我希望能够在倒计时期间重新启动 以下是我的作品: export class Timer { constructor(sec){ this.sec = sec; this.count = sec; this.running = false; } start() { this.running = true; this._run();

我有一些异步问题。我正在研究一个物体。这是一个计时器,我希望能够在倒计时期间重新启动

以下是我的作品:

export class Timer {
    constructor(sec){
        this.sec = sec;
        this.count = sec;
        this.running = false;
    }

    start() {
        this.running = true;
        this._run();
    }

    _run(){
        if(this.running){
            setTimeout(()=>{
                this.count --;
                console.log(this.count);
                if(this.count<0){
                    this.running = false;
                }
                this._run();
            }, 1000);
        }
    }

    restart(){
        this.running = false;
        /*
            Wait until _run() is done then :
        */
        this.count = this.sec;
        this.start();
    }
}
导出类计时器{
建造师(秒){
本秒=秒;
this.count=秒;
this.running=false;
}
开始(){
这是真的;
这个;
}
_运行(){
如果(这个正在运行){
设置超时(()=>{
这个.伯爵--;
console.log(this.count);

if(this.count了解计时器是否正在“运行”的更简单方法是使用
setInterval

var interval = setInterval(() => updateTimer(), 10); // update every 10ms
如果设置了
interval
,则它正在运行

if (interval) // timer is running
停止计时

window.clearInterval(interval);
interval = null;
// timer is no longer "running"
// in ~10 seconds, pause the timer
setTimeout(() => t.stop(), 10000);

附加说明

小心创建以固定值递增的计时器

在你的代码中,你有

setTimeout(() => this.count--, 1000);
其目的是让您每秒减少一次
count
属性,但这不是保证您的行为

看看这个小脚本

var state = {now: Date.now()};

function delta(now) {
  let delta = now - state.now;
  state.now = now;
  return delta;
}

setInterval(() => console.log(delta(Date.now())), 1000);

// Output
1002
1000
1004
1002
1002
1001
1002
1000
我们使用了
setInterval(fn,1000)
,但实际的间隔每次变化几毫秒

如果您将浏览器的焦点切换到其他选项卡、打开一个新选项卡等,问题就会被夸大。看看这些更零散的数字

1005 // close to 1000 ms
1005 // ...
1004 // a little variance here
1004 // ...
1834 // switched focus to previous browser tab
1231 // let timer tab run in background for a couple seconds
1082 // ...
1330 // ...
1240 // ...
2014 // switched back to timer tab
1044 // switched to previous tab
2461 // rapidly switched to many tabs below
1998 // ...
2000 // look at these numbers...
1992 // not even close to the 1000 ms that we set for the interval
2021 // ...
1989 // switched back to this tab
1040 // ...
1003 // numbers appear to stabilize while this tab is in focus
1004 // ...
1005 // ...
因此,这意味着您不能依赖于每
1000
ms运行一次
setTimeout
(或
setInterval
)函数。
count
将根据各种因素以很大的差异递减

为了解决这个问题,你需要使用一个delta。这意味着在每个“勾号”之前对于计时器,您需要使用
Date获取时间戳。现在
。在下一个刻度上,获取新的时间戳并从新的时间戳中减去以前的时间戳。这就是您的
delta
。使用此值,将其添加到计时器的总
ms
,以获得计时器已运行的精确毫秒数

然后,所有时间敏感值将是总累积ms的投影/计算

在您的情况下,假设您有一个从
10
开始的
count
。如果您想按
-1
1000
毫秒倒计时,您可以这样做

function update() {
  // update totalMs
  this.totalMs += calculateDelta();
  // display count based on totalMS
  console.log("count %d", Math.ceil(this.count - this.totalMs/1000));
}

下面是一个示例ES6计时器,它实现了一个
delta
函数,可能会对您有所帮助

class Timer {
  constructor(resolution=1000, ms=0) {
    this.ms = ms
    this.resolution = resolution;
    this.interval = null;
  }
  delta(now) {
    let delta = now - this.now;
    this.now = now;
    return delta;
  }
  start() {
    this.now = Date.now();
    this.interval = window.setInterval(() => this.update(), this.resolution);
  }
  reset() {
    this.update();
    this.ms = 0;
  }
  stop() {
    this.update();
    window.clearInterval(this.interval);
    this.interval = null;
  }
  update() {
    this.ms += this.delta(Date.now());
    console.log("%d ms - %0.2f sec", this.ms, this.ms/1000);
  }
}
创建一个分辨率为
50
ms的新计时器。这意味着计时器显示每50 ms更新一次。您可以将此值设置为任何值,计时器仍将保持准确值

var t = new Timer(50);
t.start();
为了模拟重置,我们可以创建一个一次性超时,这样您就可以看到重置工作了

// in ~5 seconds, reset the timer once
setTimeout(() => t.reset(), 5000);
下面是暂停计时器的演示

window.clearInterval(interval);
interval = null;
// timer is no longer "running"
// in ~10 seconds, pause the timer
setTimeout(() => t.stop(), 10000);
你也可以继续计时

// in ~12 seconds, resume the timer (without reset)
setTimeout(() => t.start(), 12000);
您可以
启动
停止
重置
定时器


下面是一个将ES6(以上)传输到ES5的示例,以便您可以看到代码在可运行的代码段中工作。打开控制台并单击“运行代码段”

“严格使用”;
函数_classCallCheck(实例,构造函数){if(!(构造函数的实例instanceof)){throw new TypeError(“无法将类作为函数调用”);}
变量计时器=(函数(){
函数计时器(){

var resolution=arguments.length您似乎已经在使用
running
字段维护运行状态。难道您不能只查询它的状态吗?在
restart
方法中,您可以不将
running
字段更改为false,而只检查是否已经存在这种情况。是否只想知道它是否已停止ped正在运行,还是您真的需要等待它?大多数情况下,在setTimeout()完成之前,运行会返回true。@Bergi Well start()可以启动一个新的_run()递归过程。它们将同时运行。@DanyBoisvert:这是一个问题,因为它们并不是真正“同时”运行的。相反,您的单个
计时器
实例正在以双倍速度运行:-)更干净的解决方案可能是创建一个新实例。您实际上是如何使用这些实例的?我目前看不到它们做了什么,因为它们从不向调用方发送任何消息。