I know what debounce does. I'd like to trigger it in node (by running a script with #!/usr/bin/env node), but I'm having trouble.
The code below should:
Make doThingAfterDelay() which runs a simply function after it has been called once and not been called again for 100ms.
Run doThingAfterDelay()
Sleep (asynchronously) for 15 seconds, giving doThingAfterDelay() time to debounce and therefore execute.
However it doesn't work:
var log = console.log.bind(console),
_ = require('lodash')
var doThingAfterDelay = _.debounce(function(){
return 'foo'
}, 100);
log(doThingAfterDelay());
setTimeout(function(){
log('Sleeping')
}, 15 * 1000)
It returns:
undefined
Sleeping
I expected:
foo
Sleeping
How can I make the debounced function run?
edit: I can get the desired output with:
var log = console.log.bind(console),
_ = require('lodash')
var doThingAfterDelay= _.debounce(function(){
log('foo')
}, 100);
doThingAfterDelay('one', 'two');
setTimeout(function(){
log('Sleeping')
}, 15 * 1000)
But I do not understand why - and it is important that doThingAfterDelay() returns a real value.
If you have a look at source code of the debounce function you can find that it uses setTimeout under the hood and therefore has the same mechanic. You can use a callback or Promise to pass value after debounced function will be executed(also if you are using Promise you could make your code look more synchronous with async/await).
var log = console.log.bind(console),
_ = require('lodash')
var delayedResults = new Promise(function(resolve) {
_.debounce(function(){
resolve('foo');
}, 100)();
});
var start = async function(){
log(await delayedResults )
}
start()
Related
I was writing an automatic test framework with Jest. For this, I need to run some background tasks under certain intervals. In summary, this is like polling. Let's give an example with a pseudo code.
test('Some potato poteto test', () => {
jest.setTimeout(12000000); // 20 min
const intervalPeriod = 5 * 60 * 1000; // 5 min
let retry = 0;
const intervalId = setInterval(() => {
console.log("I am doing my 5 minutes check");
// my custom logic goes here
retry++;
if(retry === MAX_RETRY) { // this will be always hit
clearInterval(intervalId)
}
}, intervalPeriod);
});
So, in every 5 mins, I'll make a network call, do some of my custom logic. Now, the problem is while running this, the test finished but jest can't terminate.
By 'test-finished' I meant, test suite ran, but the code inside the setInterval does not get executed right then.
My question is, does this happen because setInterval does not run immediately in the event-loop and is jest not configured to run a setInterval?
Please note that I don't want to mock the setInterval and also I don't want to use any fake timer.
Therefore, is it possible to achieve this with Jest?
I also tried the same thing with the cron library for node but it is the same issue. Jest does not execute the code and finish executing then halted because those setInterval/cron are still running in the background.
Is there a way to run those tasks inside Jest? I mean is there a way to run those jobs and when they are done then terminate Jest?
That's intended, Jest execute all the test instructions and quit the test.
For jest to wait for a computation, you need to use Promises and its async mode.
Doc for Jest async mode: https://jestjs.io/docs/asynchronous
Learn more about Promises here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
And async/await syntax here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
This is a naive way for doing it:
const MAX_RETRY = 3;
const INTERVAL_PERIOD = 5 * 60 * 1000; // 5 min
const SAFETY_TIME = INTERVAL_PERIOD;
const TEST_TIMEOUT = (1 + MAX_RETRY) * INTERVAL_PERIOD + SAFETY_TIME;
test('Some potato poteto test', async () => {
let retry = 0;
let success = false;
function yourLogicFunction(): boolean {
const resultOfYourLogicFunction = retry === MAX_RETRY; // because retry is used in this condition I store the result before changing it
console.log(`this is my attempt number ${retry}`);
retry++;
return resultOfYourLogicFunction;
}
function mockRequest(resolve: (value: unknown) => void): void {
setTimeout(() => resolve(yourLogicFunction()), INTERVAL_PERIOD);
}
while (!success && retry <= MAX_RETRY) {
success = await new Promise(mockRequest) as boolean;
}
}, TEST_TIMEOUT);
The third parameter of test is its timeout.
I've implemented a native function which takes a callback. NodeJS knows the interface, but it doesn't know anything about its implementation. This native function receives a callback and will call it when the result is ready. I don't want the event loop to exit while the callback hasn't been called.
Here is an example of such a problem.
Currently I need to do some I/O (even if it's a dumb timeout) to force NodeJS to wait for my function.
In Boost.Asio I'd just instantiate a work object and destroy it when the callback is called. Boost.Asio's event loop wouldn't exit while this object is kept around. Is there a similar approach for NodeJS? What do I use in NodeJS (bonus if your answer doesn't mention timers)?
The best way to do that would be writing a C++ addon and using one of the handles offered by libuv (of course, the one that fits your requirements - see the official documentation for further details).
If you don't want to do that or if you can't do that (that is the case, if I've correctly understood the question), a viable solution not mentioned in the other answers is using process.nextTick to schedule a function that checks at each tick if the loop can expires or not.
see here for further details about process.nextTick.
As a minimal, working, never-ending example:
var process = require('process')
var stop = false;
var f = function() { if(!stop) process.nextTick(f) }
f()
This way your function is in charge of setting the stop control variable once it finishes to execute, then the loop will stop.
If you have multiple callbacks to wait for, simply use a counter and check it looking for 0.
If you don't want to explicitly set and update the value of the counter each time you add a new function (error-prone), you can easily write a launcher to start your functions that increments the counter and schedules a check on the next tick if needed.
You could also pass a callback as an extra argument to your functions to notify when they are over, so that they have not to deal explicitly with the counter itself.
A plus of using a dedicated function that is scheduled on the next tick is that it's clear to the reader what you are doing.
On the other side, a fake server, a timeout scheduled in the future or an I/O stream resumed and never used are quite obscure, for the reader doesn't know immediately why you are doing that.
Method 1: Create a dummy server(updated for multiple callback):
var counter = 0;
var server = require('net').createServer().listen(); // <-- Dummy server
console.log('Dummy server start.');
var ffi = require('ffi'),
ref = require('ref')
var lib = ffi.Library('./libffi_async_demo', {
'print_thread_id': [ 'void', [] ],
'run_delayed': [ 'void', [ 'pointer' ] ],
});
var checkExit = function (){
counter--;
if (counter===0) {
server.close(); // <-- exit Dummy Server
console.log('Dummy server stop.');
}
}
// Wrapper for lib.run_delay()
run_delay = function(cb) {
counter++; // <-- increase counter
lib.run_delayed(cb);
}
var callback1 = ffi.Callback('void', [], function() {
console.log("js callback1 started");
lib.print_thread_id();
console.log("js callback1 finished");
checkExit(); // <-- call at the end of each callback
})
var callback2 = ffi.Callback('void', [], function() {
console.log("js callback2 started");
lib.print_thread_id();
console.log("js callback2 finished");
checkExit(); // <-- call at the end of each callback
})
var callback3 = ffi.Callback('void', [], function() {
console.log("js callback3 started");
lib.print_thread_id();
console.log("js callback3 finished");
checkExit(); // <-- call at the end of each callback
})
run_delay(callback1); // use wrapper
run_delay(callback2); // use wrapper
run_delay(callback3); // use wrapper
Method 2: Long timeout, callback end process
var timeout; // Hold timeout reference from setTimeout()
var ffi = require('ffi'),
ref = require('ref')
var lib = ffi.Library('./libffi_async_demo', {
'print_thread_id': [ 'void', [] ],
'run_delayed': [ 'void', [ 'pointer' ] ],
});
var callback = ffi.Callback('void', [], function() {
console.log("js callback started");
lib.print_thread_id()
console.log("js callback finished");
// Use one of the following 3:
//timeout.unref(); // <-- remove timer from Node event loop
//require('process').exit(); //<-- end process
clearTimeout(timeout); // <-- cancel timer
})
lib.run_delayed(callback)
timeout = setTimeout(function() { }, 3600000); // <-- reasonably long timeout, eg. 1hr
You can also use the stdin Readable stream to hold on exiting the loop.
const callback = ffi.Callback('void', [], function() {
// do your stuff here
// switch stream out of flowing mode.
process.stdin.pause();
});
// set stream into flowing mode ("old mode")
process.stdin.resume();
lib.run_delayed(callback);
Reference: the Note in https://nodejs.org/api/process.html#process_process_stdin
Start repl somewhere in your code, it will prevent your app from exit.
const repl = require('repl');
repl.start('> ')
When you are done just call
process.exit()
https://nodejs.org/api/repl.html
create big timeout - it'll prevent node from exiting and also guards you from waiting indefinitely for (external to node) result:
var ffi = require('ffi'),
ref = require('ref')
var ffiTimeout;
var lib = ffi.Library('./libffi_async_demo', {
'print_thread_id': [ 'void', [] ],
'run_delayed': [ 'void', [ 'pointer' ] ],
});
var ffiDidTimedOut = false;
var cancelFfi = function(timeoutCb) {
return function() {
ffiDidTimedOut = true;
timeoutCb();
}
}
var callback = ffi.Callback('void', [], function() {
if (ffiDidTimedOut) {
return; // sorry, we waited too long and already started doing something else
}
// all good, async ffi finished within expected time and we are back in our js land
clearTimeout(ffiTimeout);
lib.print_thread_id()
})
lib.run_delayed(callback)
// continueIfCancelledCallback is your continuation "what to do id ffi actually takes more than 20 seconds to run"
ffiTimeout = setTimeout(cancelFfi(continueIfCancelledCallback), 20000); // 20 seconds
I'm trying to work with synchronize.js to run some functions that must be executed synchronously.
I've read something about it here and here.
To try it out, I've created the following code
var sync = require('synchronize');
var fiber = sync.fiber;
var await = sync.await;
var defer = sync.defer;
function function1(){
console.log("Executing function 1")
}
sync.fiber(function() {
console.log('Before');
var step1 = await(setTimeout(function1, 1000, defer()));
var step2 = console.log('After');
});
console.log('Outside');
Logic tells me the output should be
Before
Outside
Executing function 1
After
But what I get is
Before
Outside
Executing function 1
Somehow, step2 is not being executed and I can't figure out why.
I've never used this plugin before but try this:
var sync = require('synchronize');
var fiber = sync.fiber;
var await = sync.await;
var defer = sync.defer;
function function1(callback){
// defer expects this to be asynchronous (so it needs to execute a callback)
console.log("Executing function 1")
callback();
}
sync.fiber(function() {
console.log('Before');
var step1 = await(setTimeout(function1, 1000, defer()));
var step2 = console.log('After');
});
console.log('Outside');
Basically I think calling defer prematurely (before the callback is executed) causes it to hang.
Edit:
After reading the documentation I now realize that defer creates a callback function. That would mean it's hanging because the callback it creates is never called. Let me know if this works.
Sometimes in nodejs I have to make a request to other host. My function have to wait until the response of this request is completed but usually they comes to next line without any waiting for the finish.
To simplify problem, I make a simple snipp code and I want to know how to get data out from this function
function testCallback(){
var _appData;
setTimeout(function(){
var a = 100;
getData(a);
}, 200);
function getData(data){
_appData = data;
}
return _appData;
}
console.log('testCallback: ', testCallback());
console.log send out undefined, I want to know with this function how to get result is 100
Basically setTimeout is asynchronous, so the code keeps going, your code it becomes, virtually like this:
function testCallback(){
var _appData;
return _appData;
}
Which, yes, it is undefined, because you do not set anything.
What you have to do is use a callback in your function, so when the long task (setTimeout) is finished you return the data.
Something like this:
function testCallback(cb){
setTimeout(function(){
var a = 100;
getData(a);
}, 200);
function getData(data){
cb(null, data);
}
}
and when calling it you have to pass a function as an argument (callback standard has 2 parameters, (err, value).)
testCallback(function(err, value) { console.log(value); });
As per Understanding the node.js event loop, node.js supports a single thread model. That means if I make multiple requests to a node.js server, it won't spawn a new thread for each request but will execute each request one by one. It means if I do the following for the first request in my node.js code, and meanwhile a new request comes in on node, the second request has to wait until the first request completes, including 5 second sleep time. Right?
var sleep = require('sleep');
sleep.sleep(5)//sleep for 5 seconds
Is there a way that node.js can spawn a new thread for each request so that the second request does not have to wait for the first request to complete, or can I call sleep on specific thread only?
If you are referring to the npm module sleep, it notes in the readme that sleep will block execution. So you are right - it isn't what you want. Instead you want to use setTimeout which is non-blocking. Here is an example:
setTimeout(function() {
console.log('hello world!');
}, 5000);
For anyone looking to do this using es7 async/await, this example should help:
const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
const example = async () => {
console.log('About to snooze without halting the event loop...');
await snooze(1000);
console.log('done!');
};
example();
In case you have a loop with an async request in each one and you want a certain time between each request you can use this code:
var startTimeout = function(timeout, i){
setTimeout(function() {
myAsyncFunc(i).then(function(data){
console.log(data);
})
}, timeout);
}
var myFunc = function(){
timeout = 0;
i = 0;
while(i < 10){
// By calling a function, the i-value is going to be 1.. 10 and not always 10
startTimeout(timeout, i);
// Increase timeout by 1 sec after each call
timeout += 1000;
i++;
}
}
This examples waits 1 second after each request before sending the next one.
Please consider the deasync module, personally I don't like the Promise way to make all functions async, and keyword async/await anythere. And I think the official node.js should consider to expose the event loop API, this will solve the callback hell simply. Node.js is a framework not a language.
var node = require("deasync");
node.loop = node.runLoopOnce;
var done = 0;
// async call here
db.query("select * from ticket", (error, results, fields)=>{
done = 1;
});
while (!done)
node.loop();
// Now, here you go
When working with async functions or observables provided by 3rd party libraries, for example Cloud firestore, I've found functions the waitFor method shown below (TypeScript, but you get the idea...) to be helpful when you need to wait on some process to complete, but you don't want to have to embed callbacks within callbacks within callbacks nor risk an infinite loop.
This method is sort of similar to a while (!condition) sleep loop, but
yields asynchronously and performs a test on the completion condition at regular intervals till true or timeout.
export const sleep = (ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* Wait until the condition tested in a function returns true, or until
* a timeout is exceeded.
* #param interval The frenequency with which the boolean function contained in condition is called.
* #param timeout The maximum time to allow for booleanFunction to return true
* #param booleanFunction: A completion function to evaluate after each interval. waitFor will return true as soon as the completion function returns true.
*/
export const waitFor = async function (interval: number, timeout: number,
booleanFunction: Function): Promise<boolean> {
let elapsed = 1;
if (booleanFunction()) return true;
while (elapsed < timeout) {
elapsed += interval;
await sleep(interval);
if (booleanFunction()) {
return true;
}
}
return false;
}
The say you have a long running process on your backend you want to complete before some other task is undertaken. For example if you have a function that totals a list of accounts, but you want to refresh the accounts from the backend before you calculate, you can do something like this:
async recalcAccountTotals() : number {
this.accountService.refresh(); //start the async process.
if (this.accounts.dirty) {
let updateResult = await waitFor(100,2000,()=> {return !(this.accounts.dirty)})
}
if(!updateResult) {
console.error("Account refresh timed out, recalc aborted");
return NaN;
}
return ... //calculate the account total.
}
It is quite an old question, and though the accepted answer is still entirely correct, the timers/promises API added in v15 provides a simpler way.
import { setTimeout } from 'timers/promises';
// non blocking wait for 5 secs
await setTimeout(5 * 1000);