Created
July 21, 2024 18:03
-
-
Save lydell/c7b93d0ad9c31139d12c187389486653 to your computer and use it in GitHub Desktop.
Explains why elm-watch has been leaking memory when hot reloading, and how to fix it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>elm-watch garbage collection</title> | |
</head> | |
<body> | |
<script> | |
(function (scope) { | |
var heavy = Array.from({ length: 1000 }, () => | |
Array.from({ length: 1000 }, () => Math.random()) | |
); | |
var view = function () { | |
console.log(heavy.length); | |
}; | |
var app = { | |
view: view, | |
}; | |
scope.Elm = { | |
Main: { | |
init: function () { | |
// Remove the closure scope of `f` and only let it access the passed things. | |
// This works because functions created with `new Function` don’t have access to | |
// the local scope, only the global scope. | |
function scopeHack(f, names, values) { | |
return new Function(...names, "return " + f.toString())( | |
...values | |
); | |
} | |
var stepper = function () { | |
app.view(); | |
}; | |
stepper = scopeHack(stepper, ["app"], [app]); | |
var hotReload = function (newApp) { | |
// Even though we re-assign the only reference to `view` that we use, | |
// `view` is still in closure scope (of `stepper` and `hotReload`) | |
// and won’t be garbage collected. Then `heavy` won’t be garbage collected either. | |
// `scopeHack` solves this problem: it removes the closure scope of `view` and `heavy`. | |
app.view = newApp.view; | |
stepper(); | |
}; | |
hotReload = scopeHack( | |
hotReload, | |
["app", "stepper"], | |
[app, stepper] | |
); | |
setInterval(stepper, 1000); | |
return { | |
hotReload: hotReload, | |
}; | |
}, | |
}, | |
}; | |
})(this); | |
var app = window.Elm.Main.init(); | |
setTimeout(() => { | |
app.hotReload({ | |
view: function () { | |
console.log("updated"); // Doesn’t reference `heavy`. | |
}, | |
}); | |
Elm.Main.init = function () { | |
console.log("new init"); | |
}; | |
}, 5000); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment