【问题标题】:How to stop asynchronous function in JavaScript?如何在 JavaScript 中停止异步函数?
【发布时间】:2015-07-17 15:20:50
【问题描述】:

我有一些异步问题。我正在处理ECMAScript 6 对象。这是一个计时器,我希望能够在倒计时期间重新启动。

这是我的工作:

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();
    }
}

restart()函数中,如何知道_run()何时停止运行?

【问题讨论】:

  • 您似乎已经使用running 字段保持运行状态。你不能只查询它的状态吗?在restart 方法中,您不必将running 字段更改为false,只需检查是否已经如此。
  • 您只想知道它是否已经停止运行,还是真的需要等待它?
  • 大多数情况下,运行在 setTimeout() 完成之前返回 true。
  • @Bergi 那么 start() 可以启动 _run() 的新递归过程。它们将同时运行。
  • @DanyBoisvert:这是个问题,因为它们并没有真正“同时”运行。相反,您的单个 Timer 实例以双倍速度运行:-) 更清洁的解决方案可能是创建一个新实例。你实际上是如何使用这些的?我目前看不到他们做什么,因为他们从不向调用者发送任何消息。

标签: javascript asynchronous settimeout


【解决方案1】:

了解计时器是否“正在运行”的更简单方法可能是改用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"

补充说明

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

在你的代码中,你有

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 // ...

因此,这意味着您不能依赖您的 setTimeout(或 setInterval)函数每 1000 毫秒运行一次。 count 将根据多种因素以很大的差异递减。

要解决此问题,您需要使用增量。这意味着在计时器的每个“滴答”之前,您需要使用Date.now 获取时间戳。在下一个刻度上,取一个新的时间戳并从新的时间戳中减去你以前的时间戳。那是你的delta。使用此值,将其添加到计时器的总 ms 以获取计时器已运行的精确毫秒数。

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

就您而言,假设您有一个以10 开头的count。如果你想倒计时-11000 毫秒,你可以这样做

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 毫秒更新一次。您可以将此值设置为任何值,并且计时器仍将保持准确的值。

var t = new Timer(50);
t.start();

为了模拟重置,我们可以创建一次性超时,以便您可以看到重置工作

// in ~5 seconds, reset the timer once
setTimeout(() => t.reset(), 5000);

这是暂停计时器的演示

// in ~10 seconds, pause the timer
setTimeout(() => t.stop(), 10000);

你也可以恢复计时器

// in ~12 seconds, resume the timer (without reset)
setTimeout(() => t.start(), 12000);

你可以start, stop, reset 定时器,随你喜欢


这是一个转译为 ES5 的 ES6(上图),因此您可以看到代码在可运行的 sn-p 中运行。打开你的控制台并点击Run code sn-p

"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Timer = (function () {
  function Timer() {
    var resolution = arguments.length <= 0 || arguments[0] === undefined ? 1000 : arguments[0];
    var ms = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];

    _classCallCheck(this, Timer);

    this.ms = ms;
    this.resolution = resolution;
    this.interval = null;
  }

  Timer.prototype.delta = function delta(now) {
    var delta = now - this.now;
    this.now = now;
    return delta;
  };

  Timer.prototype.start = function start() {
    var _this = this;

    this.now = Date.now();
    this.interval = window.setInterval(function () {
      return _this.update();
    }, this.resolution);
  };

  Timer.prototype.reset = function reset() {
    this.update();
    this.ms = 0;
  };

  Timer.prototype.stop = function stop() {
    this.update();
    window.clearInterval(this.interval);
    this.interval = null;
  };

  Timer.prototype.update = function update() {
    this.ms += this.delta(Date.now());
    console.log("%d ms - %0.2f sec", this.ms, this.ms / 1000);
  };

  return Timer;
})();

var t = new Timer(50);
t.start();

// in ~5 seconds, reset the timer once
setTimeout(function () {
  return t.reset();
}, 5000);

// in ~10 seconds, pause the timer
setTimeout(function () {
  return t.stop();
}, 10000);

// in ~12 seconds, resume the timer (without reset)
setTimeout(function () {
  return t.start();
}, 12000);

【讨论】:

    猜你喜欢
    • 2011-05-19
    • 2019-03-24
    • 2021-11-26
    • 1970-01-01
    • 1970-01-01
    • 2020-12-24
    • 1970-01-01
    • 2016-08-10
    • 2012-03-19
    相关资源
    最近更新 更多