I was wondering if someone could help me with this issue that I have...
Our client has an Legacy API which retrieves messages from users, and they want us to implement a polling mechanism for it that, based on an specific interval, updates the information within the page. Period.
They want to have a reliable polling strategy so (as you may already know) I'm using setTimeout to pull that off.
TL;DR: Does anyone one of you knows how to pull out an efficient polling utility that doesn't leak memory?
I'm trying to pull off an utility that allows me to add certain actions to a "polling list" and run the "polling" right there.
For example, I have an "actions list" similar to this:
const actions = new Map();
actions.set('1', ('1', {
action: () => {
console.log('action being executed...');
return 'a value';
},
onFinished: (...param) => { console.log(param); },
name: 'name of the action',
id: '1'
}));
I'm using Map for both api convenience and lookup performance and I'm adding a fake action to it (some of the params might not be needed but they're there for testing purposes).
Regarding the polling, I've created a delay fn to handle the timeout as a Promise (just for readability sake. It shouldn't affect the call stack usage whatsoever):
function delay(timeout) {
return new Promise(function delay(resolve) {
setTimeout(resolve, timeout);
});
}
And the polling fn that I came up with looks like this:
async function do_stuff(id, interval) {
const action = actions.get(id);
// breakout condition
if (action.status === 'stop') {
return;
}
console.log('processing...');
const response = await action.action();
console.log('executing action onFinished...');
action.onFinished(action.name, response);
delay(interval).then(function callAgain() {
do_stuff(id, interval);
});
}
I've used async/await in here because my action.action() will be mostly async operations and I'm using .then after the delay because I want to use the browser's EventTable to handle my resolve functions instead of the browser's stack. Also, I'm using named functions for debugging purposes.
To run the polling function, I just do:
const actionId = '1';
const interval = 1000;
do_stuff(actionId, interval);
And to stop the poll of that particular action, I run:
actions.get(actionId).status = 'stop'; // not fancy but effective
So far so good.... not! This surely has a ton of issues, but the one that bothers me the most of the JS Heap usage.
I ran a couple of tests using the Performance Tab from Chrome DevTools (Chrome version 64) and this is what I got:
Using an interval of 10 milliseconds
- 1000ms: polling started
- 10000ms: polling stopped
- 13000ms: ran a manual GC
Using an interval of 1 second
1000ms: polling started
10000ms: polling stopped
13000ms: ran a manual GC
Does anyone know why is this behaving like this? Why the GC it's running more frequently when I decrease the interval? Is it a memory leak or a stack issue? Are there any tools I could use to keep investigating about this issue?
Thanks in advance!
Stuff that I've read:
http://reallifejs.com/brainchunks/repeated-events-timeout-or-interval/ (why choosing setTimeout instead of setInterval)
Building a promise chain recursively in javascript - memory considerations
How do I stop memory leaks with recursive javascript promises?
How to break out of AJAX polling done using setTimeout
https://alexn.org/blog/2017/10/11/javascript-promise-leaks-memory.html
PS: I've putted the snippet right here in case anyone wants to give it a try.
const actions = new Map();
actions.set('1', ('1', {
action: () => {
console.log('action being executed...');
return 'a value';
},
onFinished: (...param) => { console.log(param); },
name: 'name of the action',
id: '1'
}));
function delay(timeout) {
return new Promise(function delay(resolve) {
setTimeout(resolve, timeout);
});
}
async function do_stuff(id, interval) {
const action = actions.get(id);
// breakout condition
if (action.status === 'stop') {
return;
}
console.log('processing...');
const response = await action.action();
console.log('executing action onFinished...');
action.onFinished(action.name, response);
delay(interval).then(function callAgain() {
do_stuff(id, interval);
});
}
/*
// one way to run it:
do_stuff('1', 1000);
// to stop it
actions.get('1').status = 'stop';
*/
Related
I'm trying to do something that involves a global context that knows about what function is running at the moment.
This is easy with single-threaded synchronous functions, they start off running and finish running when they return.
But async functions can pop to the bottom of the program and climb back up multiple times before completing.
let currentlyRunningStack: string[] = [];
function run(id: string, cb: () => any) {
currentlyRunningStack.push(id);
cb()
currentlyRunningStack.shift();
}
// works with synchronous stuff
run("foo", () => {
run("bar", () => console.log("bar should be running"));
console.log("now foo is running");
});
// can it work with asynchronous
run("qux", async () => {
// async functions never run immediately...
await somePromise();
// and they start and stop a lot
});
Is it possible to keep track of whether or not an asynchronous function is currently running or currently waiting on something?
EDIT: there appears to be something similar called Zone.js. Used by Angular I guess.
Something like async_hooks for the browser?
EDIT: per #Bergi's suggestion, the word "stack" has been updated to "program" for clarification
It is possible, and someone has done it -- the angular developers, no less -- but it costs a whopping 5.41Mb!!
https://www.npmjs.com/package/zone.js
This was ripped from this very similar question: Something like async_hooks for the browser?
In order to distinguish from that question a bit, I'll answer the core query here:
Yes, you can tell when an async function stops and starts. You can see a lot of what's required in the project code
In particular, this file appears to handle poly-filling promises, though I'd need more time to verify this is where the magic happens. Perhaps with some effort I can distill this into something simpler to understand, that doesn't require 5.41 Mb to acheive.
Yes, it possible.
Using a running context, like a mutex, provided by Edgar W. Djiskistra, stack queues, Promise states and Promise.all executor. This way you can keep track if there's a running function in the program. You will have to implement a garbage collector, keeping the mutex list clean and will need a timer(setTimeout) to verify if the context is clean. When the context is clean, you will call a callback-like function to end you program, like process.exit(0). By context we refeer to the entire program order of execution.
Transforming the function into a promise with an .then callback to pop/clean the stack of the mutex after the execution of content of the function with a try/catch block to throw, handle, or log errors add more control to the hole program.
The introduction of setTimeout propicies a state machine, combined with the mutex/lock and introduces a memory leak that you will need to keep track of the timer to clear the memory allocated by each function.
This is done by neste try/catch. The use of setInterval for it introduces a memory leak that will cause a buffer overflow.
The timer will do the end of the program and there's it. You can keep track if a function is running or not and have every function registered running in a syncrhonous manner using await with and mutex.
Running the program/interpreter in a syncrhonous way avoid the memory leaks and race conditions, and work well. Some code example below.
const async run (fn) => {
const functionContextExecutionStackLength = functionExecutionStackLength + 1
const checkMutexStackQueue = () => {
if (mutexStack[0] instanceof Promise) {
if (mutex[0].state == "fullfiled") {
mutexStack = mutexStack.split(1, mutexStack.length)
runner.clear()
runner()
}
}
if (mutexStack.length == 0) process.exit(0)
}
// clear function Exection Context
const stackCleaner = setTimeout(1000, (err, callback) => {
if (functionContextExecutionStackLength == 10) {
runner.clear()
}
})
stackCleaner = stackCleaner()
// avoid memory leak on function execution context
if (functionContextExecutionStackLength == 10) {
stackCleaner.clear()
stackCleaner()
}
// the runner
const runner = setTimeout(1, async (err, callback) => {
// run syncronous
const append = (fn) => mutex.append(util.promisfy(fn)
.then(appendFunctionExectionContextTimes)
.then(checkMutexStackQueue))
// tranaform into promise with callback
const fn1 = () => util.promify(fn)
const fn2 = () => util.promisfy(append)
const orderOfExecution = [fn1, fn2, fn]
// a for await version can be considered
for (let i = 0; i < orderOfExecution.length; i++) {
if (orderOfExecution.length == index) {
orderOfExecution[orderOfExecution.length]()
} else {
try {
orderOfExecution[i]()
} catch (err) {
throw err
console.error(err)
}
}
}
}
}
(() => run())(fn)
On the code above we take the assynchronous caracterisc of javascript very seriously. Avoiding it when necessary and using it when is needed.
Obs:
Some variables was ommited but this is a demo code.
Sometimes you will see a variables context switching and call before execution, this is due to the es modules characteriscts of reading it all and interpreting it later.
So I've been trying to google my answer but had no luck finding it. I'm wondering how I can await a value to be defined by the use of promises, not a brute-force method like using:
while(true){ Check for condition and then break }
This was my initial brute-force approach but It obviously isn't a good solution:
const awaitToken = new Promise((resolve, reject) => {
while(true){
if(getState().auth.token){
resolve(true)
break;
}
}
})
**
EDIT: The getState method isn't asynchronous otherwise it would be way easier and why I'm implementing this is because there is a small delay from when the user is signed in to when the token is available in redux state
**
I'm working on a react-redux project where I want to implement this however, my code is irrelevant because I don't want a case-specific answer. I would appreciate any help!
Here is a non-blocking solution for using synced functions an easy way in promises.
An additional timeout can save you a lot of waiting time.
let arrivedToken = new Promise((resolve, reject) => {
/* how often you wanna check? */
let interval = 20;
/*how long do you want to wait? */
let timeOut = 4000;
let timer = setInterval(() => {
timeOut -= interval;
if (timeOut < 1) {
clearInterval(timer);
reject(new Error('catching token timed out'));
}
let token = getState().auth.token;
if (token)
resolve(token);
}, interval);
};
so you can handle the success or even the failure:
arrivedToken
.then(token => {
/* whatever you want */
})
.catch(error => {
/* handle the timeout */
})
;
Redux allows you to subscribe to a store. The callback will be called whenever an action is dispatched.
Meaning you could do something like this:
const awaitToken = new Promise(resolve => {
if (getState().auth.token) return resolve(true);
const unsubscribe = subscribe(() => {
if (!getState().auth.token) return;
unsubscribe();
resolve(true);
});
});
Additional info about subscribe can be found here. You might also want to add a timeout, but that might depend on the use case.
Push vs. Pull
I'm going to make some assumptions given the way you asked your question: The assumption is no-one has explained the Push v Pull dichotomy. It's extremely important to understand to solve these types of problems in the future without SO's community.
Promises and callbacks were designed by the ECMA body to enable developers to PULL for results of asynchronous tasks. You can think of PULL-ing as picking up the telephone and calling your friend asking him if your package has been delivered at his address that you used on your delivery form. Obviously you're not going to stay on the call until the package arrives so you continuously have to iterate this process until your friend says, "yes, you're package has arrived" (e.g. Promise.resolve(package) || cb('package has arrived')).
Then the ECMA body delivered us ES8. And with it, a PUSH methodology which solves exactly the problem that you're beating your head against quite admirably I might add. The async/await dichotomy enables a developer to no longer have to pick up the phone and call their friend. Instead, your friend picks up the phone and calls you and says "Hey man, come get your stuff off my porch, it's arrived." This is a PUSH. async/await & generators
More Verbosity on async/await theory if you desire here
Solutions
generators
function * fetchToken() {
const { auth } = getToken(); // getToken() is asynchronous and BLOCKs control flow until value is returned.
yield auth.token;
}
const tokenGen = fetchToken();
const { value: token } = tokenGen().next();
console.log('token: ', token); // [jwt token]
async/await
async function fetchToken() {
const { auth } = await getToken(); // await BLOCK's control flow until the function resolves.
return auth.token;
}
const token = fetchToken();
console.log('token: ', token); // [jwt token]
In my JavaScript code I have some function that I call a 'worker' that checks if it is started and do some work
class Application
{
async runWorker()
{
while (true)
{
while (!this.isStarted)
{
await new Promise(resolve => setTimeout(resolve, 1000));
}
//do some work
this.DoWork();
}
}
}
I run the worker with the application starts by simply calling
this.runWorker();
and set this.isStarted to true or false to start or stop it.
This works fine, but there is some obvious disadvantage: it can take up to a second (1000ms) until this.DoWork() is called when this.isStarted is changed from false to true.
Is there a mechanism in JavaScript that allows to start and stop the worker immediately? Or probably a way to rewrite this code somehow?
For example, in C++ I would create a separate thread that sleeps when the worker is stopped and use what is called 'event synchronization primitive', but I have no idea on how to implement this scenario in JavaScript (node.js).
You could use a flag to indicate that the loop is supposed to continue. Then call the looping function directly:
const app = {
run: false,
async doWork() {
if(this.run) return; // Don't run twice
this.run = true;
while(this.run) {
await Library.stuff();
}
},
cancel() { this.run = false; },
};
app.doWork();
// somewhen
app.cancel();
As I expect you're aware, JavaScript only has one thread. You can't use threading techniques you might use in other languages for this sort of thing.
However, the obvious question is: can you just call DoWork() directly where you set the isStarted flag to true (I mean instead of setting the flag)? That will start the method immediately, clearly.
You may not be able to. DoWork is a longrunning process and you are interrupting the code where you make the call, and it will only continue once the method returns. One way around this is to use setTimeout(DoWork, 0). This puts the call onto the message loop, and it will execute once the currently-executing code has finished. That isn't 'immediately' but it may be 'at a safe time to do it'.
Consider a sequence of steps that need to be performed as part of preparing a heavy web page:
step1();
step2();
..
stepk();
Each step may take in the range of 100milliseconds to a few seconds: but we are uncertain in advance how long each step takes.
At least until Promise/ await hit the street my understanding is that we use callbacks along with setTimeout.
But how can we avoid that from quickly becoming unwieldy? In the following sequence we have two concerns:
how to specify the timeout when the actual work could be up to two orders of magnitude in range
how to handle the passing of arguments - argK in the code shown below - to the nested function invocations
First two steps (of K):
function step1(args1,args2,args3,..) {
// do work for step1 using args1
setTimeout(function() {step2(args2,args3);}, [some timeout..]);
}
function step2(args2,args3,..) {
// do work for step2 using args2
setTimeout(function() {step3(args3 [, args4, args5 ..]);}, [some timeout..]);
}
So how can these sequential steps be structured so that we are not sending a growing list of args down an entire chain of functions?
Note: Webworkers may be a useful approach for some cases: but I want to be able to serve from the local file system and that apparently precludes them:
http://blog.teamtreehouse.com/using-web-workers-to-speed-up-your-javascript-applications
Restricted Local Access
Web Workers will not work if the web page is being served directly
from the filesystem (using file://). Instead you will need to use a
local development server such as XAMPP.
Without promises or async await, you must do it callback hell style
function step1(a,b,c){
setTimeout(() => {
step2():
}
}
Or you can pass references to the next step
If step2 relies on results from step1
function step1(a,b,c, done){
setTimeout(() => {
done(a,b,step3):
}
}
function step2(d,e,done){
setTimeout(() => {
done(e):
}
}
step1("cat","dog","mouse", step2);
If you want to pass args to step2 manually, and get results from step1
function step1(a,b,c, done){
setTimeout(() => {
done(a):
}
}
function step2(d,e,done){
return function(step1a){
setTimeout(() => {
done(step1a, d):
}
}
}
step1("cat","dog","mouse", step2("d","e", step3);
This is probably as clean as you can get without Promisifying your async actions or implementing your own promise style.
I've been reading up on generator functions and it seems they could be a vanilla JS solution.
Alex Perry wrote a great article with a relevant demo:
function step1() {
setTimeout(function(){
gen.next('data from 1')
}, 500);
}
function step2(data) {
setTimeout(function(){
gen.next(`data from 2 and ${data[0]}`)
}, 700);
}
function step3() {
setTimeout(function(){
gen.next('data from 3')
}, 100);
}
function *sayHello() {
var data = [];
data.push(yield step1());
data.push(yield step2(data));
data.push(yield step3(data));
console.log(data);
}
var gen = sayHello();
gen.next();
In the example above each asynchronous request returns a fake data. Each successive step receives an array containing the previous responses so the previous responses can be used.
I have such a function in my JS script:
function heavyWork(){
for (i=0; i<300; i++){
doSomethingHeavy(i);
}
}
Maybe "doSomethingHeavy" is ok by itself, but repeating it 300 times causes the browser window to be stuck for a non-negligible time. In Chrome it's not that big of a problem because only one Tab is effected; but for Firefox its a complete disaster.
Is there any way to tell the browser/JS to "take it easy" and not block everything between calls to doSomethingHeavy?
You could nest your calls inside a setTimeout call:
for(...) {
setTimeout(function(i) {
return function() { doSomethingHeavy(i); }
}(i), 0);
}
This queues up calls to doSomethingHeavy for immediate execution, but other JavaScript operations can be wedged in between them.
A better solution is to actually have the browser spawn a new non-blocking process via Web Workers, but that's HTML5-specific.
EDIT:
Using setTimeout(fn, 0) actually takes much longer than zero milliseconds -- Firefox, for example, enforces a minimum 4-millisecond wait time. A better approach might be to use setZeroTimeout, which prefers postMessage for instantaneous, interrupt-able function invocation, but use setTimeout as a fallback for older browsers.
You can try wrapping each function call in a setTimeout, with a timeout of 0. This will push the calls to the bottom of the stack, and should let the browser rest between each one.
function heavyWork(){
for (i=0; i<300; i++){
setTimeout(function(){
doSomethingHeavy(i);
}, 0);
}
}
EDIT: I just realized this won't work. The i value will be the same for each loop iteration, you need to make a closure.
function heavyWork(){
for (i=0; i<300; i++){
setTimeout((function(x){
return function(){
doSomethingHeavy(x);
};
})(i), 0);
}
}
You need to use Web Workers
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
There are a lot of links on web workers if you search around on google
We need to release control to the browser every so often to avoid monopolizing the browser's attention.
One way to release control is to use a setTimeout, which schedules a "callback" to be called at some period of time. For example:
var f1 = function() {
document.body.appendChild(document.createTextNode("Hello"));
setTimeout(f2, 1000);
};
var f2 = function() {
document.body.appendChild(document.createTextNode("World"));
};
Calling f1 here will add the word hello to your document, schedule a pending computation, and then release control to the browser. Eventually, f2 will be called.
Note that it's not enough to sprinkle setTimeout indiscriminately throughout your program as if it were magic pixie dust: you really need to encapsulate the rest of the computation in the callback. Typically, the setTimeout will be the last thing in a function, with the rest of the computation stuffed into the callback.
For your particular case, the code needs to be transformed carefully to something like this:
var heavyWork = function(i, onSuccess) {
if (i < 300) {
var restOfComputation = function() {
return heavyWork(i+1, onSuccess);
}
return doSomethingHeavy(i, restOfComputation);
} else {
onSuccess();
}
};
var restOfComputation = function(i, callback) {
// ... do some work, followed by:
setTimeout(callback, 0);
};
which will release control to the browser on every restOfComputation.
As another concrete example of this, see: How can I queue a series of sound HTML5 <audio> sound clips to play in sequence?
Advanced JavaScript programmers need to know how to do this program transformation or else they hit the problems that you're encountering. You'll find that if you use this technique, you'll have to write your programs in a peculiar style, where each function that can release control takes in a callback function. The technical term for this style is "continuation passing style" or "asynchronous style".
You can make many things:
optimize the loops - if the heavy works has something to do with DOM access see this answer
if the function is working with some kind of raw data use typed arrays MSDN MDN
the method with setTimeout() is called eteration. Very usefull.
the function seems to be very straight forward typicall for non-functional programming languages. JavaScript gains advantage of callbacks SO question.
one new feature is web workers MDN MSDN wikipedia.
the last thing ( maybe ) is to combine all the methods - with the traditional way the function is using only one thread. If you can use the web workers, you can divide the work between several. This should minimize the time needed to finish the task.
I see two ways:
a) You are allowed to use Html5 feature. Then you may consider to use a worker thread.
b) You split this task and queue a message which just do one call at once and iterating as long there is something to do.
There was a person that wrote a specific backgroundtask javascript library to do such heavy work.. you might check it out at this question here:
Execute Background Task In Javascript
Haven't used that for myself, just used the also mentioned thread usage.
function doSomethingHeavy(param){
if (param && param%100==0)
alert(param);
}
(function heavyWork(){
for (var i=0; i<=300; i++){
window.setTimeout(
(function(i){ return function(){doSomethingHeavy(i)}; })(i)
,0);
}
}())
There is a feature called requestIdleCallback (pretty recently adopted by most larger platforms) where you can run a function that will only execute when no other function takes up the event loop, which means for less important heavy work you can execute it safely without ever impacting the main thread (given that the task takes less than 16ms, which is one frame. Otherwise work has to be batched)
I wrote a function to execute a list of actions without impacting main thread. You can also pass a shouldCancel callback to cancel the workflow at any time. It will fallback to setTimeout:
export const idleWork = async (
actions: (() => void)[],
shouldCancel: () => boolean
): Promise<boolean> => {
const actionsCopied = [...actions];
const isRequestIdleCallbackAvailable = "requestIdleCallback" in window;
const promise = new Promise<boolean>((resolve) => {
if (isRequestIdleCallbackAvailable) {
const doWork: IdleRequestCallback = (deadline) => {
while (deadline.timeRemaining() > 0 && actionsCopied.length > 0) {
actionsCopied.shift()?.();
}
if (shouldCancel()) {
resolve(false);
}
if (actionsCopied.length > 0) {
window.requestIdleCallback(doWork, { timeout: 150 });
} else {
resolve(true);
}
};
window.requestIdleCallback(doWork, { timeout: 200 });
} else {
const doWork = () => {
actionsCopied.shift()?.();
if (shouldCancel()) {
resolve(false);
}
if (actionsCopied.length !== 0) {
setTimeout(doWork);
} else {
resolve(true);
}
};
setTimeout(doWork);
}
});
const isSuccessful = await promise;
return isSuccessful;
};
The above will execute a list of functions. The list can be extremely long and expensive, but as long as every individual task is under 16ms it will not impact main thread. Warning because not all browsers supports this yet, but webkit does