如何在JavaScript中停止异步功能? [英] How to stop asynchronous function in JavaScript?

查看:96
本文介绍了如何在JavaScript中停止异步功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have some asynchronous problems. I'm working on an ECMAScript 6 object. It's a timer and I want to be able to restart during its countdown.

这是我的工作:

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()何时停止运行?

In the restart() function, how can I know when _run() has stopped running?

推荐答案

了解计时器是否正在运行的更简单方法是使用 setInterval 代替。

An easier way to know if the timer is "running" is to perhaps use setInterval instead.

var interval = setInterval(() => updateTimer(), 10); // update every 10ms

如果 interval 已设置

if (interval) // timer is running

停止计时器

window.clearInterval(interval);
interval = null;
// timer is no longer "running"






附加说明


Additional notes


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

Beware of creating timers that increment with a fixed value

在您的代码中,您有

setTimeout(() => this.count--, 1000);

目的是减少次数财产每秒一次,但这不是你将得到保证的行为。

The intention is for you to decrement your count property once every second, but this is not the behavior you will be guaranteed.

查看这个小脚本

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)但实际间隔每次都会变化几毫秒。

We used setInterval(fn, 1000) but the actual interval varies a couple milliseconds each time.

如果您将浏览器的焦点切换到其他标签,打开新标签,则会夸大问题,看看这些更零星的数字

The problem is exaggerated if you do things like switch your browser's focus to a different tab, open a new tab, etc. Look at these more sporadic numbers

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 ms运行一次。 count 将根据各种因素递减多少差异。

So, this means you can't rely upon your setTimeout (or setInterval) function getting run once per 1000 ms. count will be decremented with much variance depending on a wide variety of factors.

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

To work around this, you need to use a delta. That means before each "tick" of your timer, you need to take a timestamp using Date.now. On the next tick, take a new timestamp and subtract your previous timestamp from the new one. That is your delta. Using this value, add it to the Timer's total ms to get the precise number of milliseconds the timer has been running for.

然后,所有对时间敏感的值都是累计总积分的预测/计算。

Then, all time-sensitive values will be a projection/calculation of the total accumulated ms.

在你的情况下,说你从 10 开始的计数。如果你想按 -1 倒计时每个 1000 ms,你可以做

In your case, say you have a count that starts at 10. If you want to count down by -1 each 1000 ms, you could do

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

Create a new timer with a 50 ms "resolution". All this means is that the timer display is updated every 50 ms. You could set this value to anything and the timer will still keep an accurate value.

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

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

To simulate the reset, we can just create a one-off timeout so you can see the reset working

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

以下是暂停计时器的演示

Here's a demonstration of pausing the timer

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

你也可以恢复计时器

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

您可以开始停止重置计时器尽可能多

You can start, stop, reset the timer as much as you like

这是ES6(上面)转换为ES5,因此您可以看到代码在可运行的代码段中运行。打开控制台,然后单击运行代码段

Here's an the ES6 (above) transpiled to ES5 so you can see the code working in a runnable snippet. Open your console and click Run code snippet.

"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);

这篇关于如何在JavaScript中停止异步功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆