Javascript递归,内存泄漏?
Javascript recursion, memory leak?
我正在尝试实现每秒执行一次操作的 class,它按预期工作,但我不确定内存泄漏。我正在编写的这段代码将有几个月的正常运行时间。
下面的代码会不会导致内存泄漏,因为它在技术上是永不结束的递归?
class Algorithm{
constructor(){
//there will be many more things in this constructor
//which is why this is a class
const pidTimer = (r,e=0,y=Date.now()-r) => {
this.someFunction();
const now = Date.now();
const dy = now-y;
const err = e+r-dy
const u = err*0.2;
//console.log(dy)
setTimeout(()=>{pidTimer(r,err,now)},r+u);
}
pidTimer(1000);
}
someFunction = () => {}
}
这不是那种在setTimeout()
触发并再次调用pidTimer()
之前pidTimer()
函数调用returns之后有任何堆栈累积的递归。我什至不会调用这个递归(它被安排重复调用),但那更像是一个语义问题。
所以,我看到的唯一可能存在内存泄漏或过度使用的地方是 this.someFunction();
内部,那只是因为您没有向我们展示那里的代码来评估它并查看它是什么做。您向我们展示的 pidTimer()
代码本身没有问题。
现代异步原语
您当前的功能没有任何“错误”,但我认为它可以得到显着改进。 JavaScript 提供了一个现代化的异步原子,Promise and new syntax support async/await。这些比石器时代的 setTimeout
和 setInterval
更受欢迎,因为您可以轻松地通过异步控制流对数据进行线程化,停止考虑“回调”,并避免副作用 -
class Algorithm {
constructor() {
...
this.runProcess(...)
}
async runProcess(...) { // async
while (true) { // loop instead of recursion
await sleep(...) // sleep some amount of time
this.someFunction() // do work
... // adjust timer variables
}
}
}
sleep
是一个简单的函数,它在指定的毫秒值后解析一个 promise,ms
-
function sleep(ms) {
return new Promise(r => setTimeout(r, ms)) // promise
}
异步迭代器
但是看看 this.someFunction()
怎么 return 什么都没有?如果我们可以从 someFunction
捕获数据并将其提供给我们的调用者,那就太好了。通过使 runProcess
成为 async generator and implementing Symbol.asyncIterator 我们可以轻松处理异步 和 停止副作用 -
class Algorithm {
constructor() {
...
this.data = this.runProcess(...) // assign this.data
}
async *runProcess(...) { // async generator
while (true) {
await sleep(...)
yield this.someFunction() // yield
...
}
}
[Symbol.asyncIterator]() { // iterator
return this.data
}
}
现在调用者可以控制数据从 this.someFunction
传入时发生的情况。下面我们写入 console.log
,但您可以轻松地将其交换为 API 调用或写入文件系统 -
const foo = new Algorithm(...)
for await (const data of foo)
console.log("process data", data) // or API call, or write to file system, etc
附加控制
您可以通过使用额外的数据成员轻松地添加对进程的控制。下面我们用条件换出 while(true)
并允许调用者停止进程 -
class Algorithm {
constructor() {
...
}
async *runProcess(...) {
this.running = true // start
while (this.running) { // conditional loop
...
}
}
haltProcess() {
this.running = false // stop
}
...
}
演示
这是一个包含上述概念的功能演示。注意我们这里只实现 halt
因为 run
是一个 infinite 生成器。有限生成器不需要手动停止。通过 运行 代码段 -
在您自己的浏览器中验证结果
class Algorithm {
async *run() {
this.running = true
while(this.running) {
await sleep(1000)
yield this.someFunction()
}
}
halt() {
this.running = false
}
someFunction() {
return Math.random()
}
[Symbol.asyncIterator] = this.run
}
function sleep(ms) {
return new Promise(r => setTimeout(r, ms))
}
async function main() {
const foo = new Algorithm // init
setTimeout(_ => foo.halt(), 10000) // stop at some point, for demo
for await (const x of foo) // iterate
console.log("data", x) // log, api call, write fs, etc
return "done" // return something when done
}
main().then(console.log, console.error) // "done"
data 0.3953947360028206
data 0.18754462176783115
data 0.23690422070864803
data 0.11237466374294014
data 0.5123244720637253
data 0.39818889343799635
data 0.08627407687877853
data 0.3861902404922477
data 0.8358471443658225
data 0.2770336562516085
done
我正在尝试实现每秒执行一次操作的 class,它按预期工作,但我不确定内存泄漏。我正在编写的这段代码将有几个月的正常运行时间。
下面的代码会不会导致内存泄漏,因为它在技术上是永不结束的递归?
class Algorithm{
constructor(){
//there will be many more things in this constructor
//which is why this is a class
const pidTimer = (r,e=0,y=Date.now()-r) => {
this.someFunction();
const now = Date.now();
const dy = now-y;
const err = e+r-dy
const u = err*0.2;
//console.log(dy)
setTimeout(()=>{pidTimer(r,err,now)},r+u);
}
pidTimer(1000);
}
someFunction = () => {}
}
这不是那种在setTimeout()
触发并再次调用pidTimer()
之前pidTimer()
函数调用returns之后有任何堆栈累积的递归。我什至不会调用这个递归(它被安排重复调用),但那更像是一个语义问题。
所以,我看到的唯一可能存在内存泄漏或过度使用的地方是 this.someFunction();
内部,那只是因为您没有向我们展示那里的代码来评估它并查看它是什么做。您向我们展示的 pidTimer()
代码本身没有问题。
现代异步原语
您当前的功能没有任何“错误”,但我认为它可以得到显着改进。 JavaScript 提供了一个现代化的异步原子,Promise and new syntax support async/await。这些比石器时代的 setTimeout
和 setInterval
更受欢迎,因为您可以轻松地通过异步控制流对数据进行线程化,停止考虑“回调”,并避免副作用 -
class Algorithm {
constructor() {
...
this.runProcess(...)
}
async runProcess(...) { // async
while (true) { // loop instead of recursion
await sleep(...) // sleep some amount of time
this.someFunction() // do work
... // adjust timer variables
}
}
}
sleep
是一个简单的函数,它在指定的毫秒值后解析一个 promise,ms
-
function sleep(ms) {
return new Promise(r => setTimeout(r, ms)) // promise
}
异步迭代器
但是看看 this.someFunction()
怎么 return 什么都没有?如果我们可以从 someFunction
捕获数据并将其提供给我们的调用者,那就太好了。通过使 runProcess
成为 async generator and implementing Symbol.asyncIterator 我们可以轻松处理异步 和 停止副作用 -
class Algorithm {
constructor() {
...
this.data = this.runProcess(...) // assign this.data
}
async *runProcess(...) { // async generator
while (true) {
await sleep(...)
yield this.someFunction() // yield
...
}
}
[Symbol.asyncIterator]() { // iterator
return this.data
}
}
现在调用者可以控制数据从 this.someFunction
传入时发生的情况。下面我们写入 console.log
,但您可以轻松地将其交换为 API 调用或写入文件系统 -
const foo = new Algorithm(...)
for await (const data of foo)
console.log("process data", data) // or API call, or write to file system, etc
附加控制
您可以通过使用额外的数据成员轻松地添加对进程的控制。下面我们用条件换出 while(true)
并允许调用者停止进程 -
class Algorithm {
constructor() {
...
}
async *runProcess(...) {
this.running = true // start
while (this.running) { // conditional loop
...
}
}
haltProcess() {
this.running = false // stop
}
...
}
演示
这是一个包含上述概念的功能演示。注意我们这里只实现 halt
因为 run
是一个 infinite 生成器。有限生成器不需要手动停止。通过 运行 代码段 -
class Algorithm {
async *run() {
this.running = true
while(this.running) {
await sleep(1000)
yield this.someFunction()
}
}
halt() {
this.running = false
}
someFunction() {
return Math.random()
}
[Symbol.asyncIterator] = this.run
}
function sleep(ms) {
return new Promise(r => setTimeout(r, ms))
}
async function main() {
const foo = new Algorithm // init
setTimeout(_ => foo.halt(), 10000) // stop at some point, for demo
for await (const x of foo) // iterate
console.log("data", x) // log, api call, write fs, etc
return "done" // return something when done
}
main().then(console.log, console.error) // "done"
data 0.3953947360028206
data 0.18754462176783115
data 0.23690422070864803
data 0.11237466374294014
data 0.5123244720637253
data 0.39818889343799635
data 0.08627407687877853
data 0.3861902404922477
data 0.8358471443658225
data 0.2770336562516085
done