如何在 JavaScript 中停止异步函数?
How to stop asynchronous function in JavaScript?
我有一些异步问题。我正在处理 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" 的更简单方法可能是使用 setInterval
。
var interval = setInterval(() => updateTimer(), 10); // update every 10ms
如果设置了interval
,则为运行ning
if (interval) // timer is running
停止计时器
window.clearInterval(interval);
interval = null;
// timer is no longer "running"
补充说明
Beware of creating timers that increment with a fixed value
在您的代码中,您有
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
会因多种因素而减少,差异很大。
要解决此问题,您需要使用增量。这意味着在计时器的每个 "tick" 之前,您需要使用 Date.now
获取时间戳。在下一次报价中,获取一个新的时间戳并从新时间戳中减去之前的时间戳。那就是你的delta
。使用此值,将其添加到计时器的总计 ms
以获得计时器已 运行 的精确 毫秒数。
然后,所有时间敏感值将是总累计毫秒数的 projection/calculation。
在你的例子中,假设你有一个从 10
开始的 count
。如果你想每 1000
毫秒倒数 -1
,你可以做
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
毫秒 "resolution" 的新计时器。这意味着定时器显示每 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(上图),因此您可以看到代码在 运行nable 片段中运行。打开您的控制台并单击 运行 代码段。
"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);
我有一些异步问题。我正在处理 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" 的更简单方法可能是使用 setInterval
。
var interval = setInterval(() => updateTimer(), 10); // update every 10ms
如果设置了interval
,则为运行ning
if (interval) // timer is running
停止计时器
window.clearInterval(interval);
interval = null;
// timer is no longer "running"
补充说明
Beware of creating timers that increment with a fixed value
在您的代码中,您有
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
会因多种因素而减少,差异很大。
要解决此问题,您需要使用增量。这意味着在计时器的每个 "tick" 之前,您需要使用 Date.now
获取时间戳。在下一次报价中,获取一个新的时间戳并从新时间戳中减去之前的时间戳。那就是你的delta
。使用此值,将其添加到计时器的总计 ms
以获得计时器已 运行 的精确 毫秒数。
然后,所有时间敏感值将是总累计毫秒数的 projection/calculation。
在你的例子中,假设你有一个从 10
开始的 count
。如果你想每 1000
毫秒倒数 -1
,你可以做
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
毫秒 "resolution" 的新计时器。这意味着定时器显示每 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(上图),因此您可以看到代码在 运行nable 片段中运行。打开您的控制台并单击 运行 代码段。
"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);