How to measure the execution time of an asynchronous function in nodejs? - javascript

I'm trying to get the execution/response time of an asynchronous function that executes inside a node-fetch operation, like the following
async function getEditedData() {
var a = await fetch(`https://api.example.com/resorce_example`);
var b = await a.json();
// Some aditional treatment of the object obtained (b)
console.log("End of the asynchronous function")
}
I Used the library perf_hooks like this, but the execution time shows before
const hrtime = require ('perf_hooks').performance.now ;
var start = hrtime ();
getEditedData();
var end = hrtime ();
console.log (end - start);
I found the async_hooks library https://nodejs.org/api/perf_hooks.html#perf_hooks_measuring_the_duration_of_async_operations , but I canĀ“t understand how it works. I am a basic in javascript/nodejs

You could simply store Date.now() in some variable and then check Date.now() when your Promise resolves (or rejects) and subtract to find the difference. For example:
const simulateSomeAsyncFunction = new Promise((resolve, reject) => {
console.log('Initiating some async process, please wait...')
const startTime = Date.now();
setTimeout(() => {
resolve(Date.now() - startTime);
}, 3000);
});
simulateSomeAsyncFunction.then(msElapsed => {
console.log(`Async function took ${msElapsed / 1000} seconds to complete.`);
});
Note: You could write code that achieves the same thing and appears to be synchronous by using await/async since that is just "syntactic sugar" built on top of Promises. For example:
const simulateSomeAsyncFunction = () => {
console.log('Initiating some async process, please wait...');
return new Promise((resolve, reject) => {
setTimeout(resolve, 3000);
});
};
// Await is required to be called within an async function so we have to wrap the calling code in an async IIFE
(async() => {
const startTime = Date.now();
await simulateSomeAsyncFunction();
const msElapsed = Date.now() - startTime;
console.log(`Async function took ${msElapsed / 1000} seconds to complete.`);
})();

Starting with a simple async function -
const fakeAsync = async (value) => {
const delay = 2000 + Math.random() * 3000 // 2 - 5 seconds
return new Promise(r => setTimeout(r, delay, value))
}
fakeAsync("foo").then(console.log)
console.log("please wait...")
// "please wait..."
// "foo"
We could write a generic function, timeit. This is a higher-order function that accepts a function as input and returns a new function as output. The new function operates like a decorated version of the original -
const timeit = (func = identity) => async (...args) => {
const t = Date.now()
const result = await func(...args)
return { duration: Date.now() - t, result }
}
// decorate the original
const timedFakeAsync = timeit(fakeAsync)
// call the decorated function
timedFakeAsync("hello").then(console.log)
timedFakeAsync("earth").then(console.log)
// { duration: 3614, result: "earth" }
// { duration: 4757, result: "hello" }
The timed version of our function returns an object, { duration, result }, that reports the runtime of our async function and the result.
Expand the snippet below to verify the results in your own browser -
const identity = x =>
x
const timeit = (func = identity) => async (...args) => {
const t = Date.now()
const result = await func(...args)
return { duration: Date.now() - t, result }
}
const fakeAsync = async (value) => {
const delay = 2000 + Math.random() * 3000 // 2 - 5 seconds
return new Promise(r => setTimeout(r, delay, value))
}
const timedFakeAsync = timeit(fakeAsync)
timedFakeAsync("hello").then(console.log)
timedFakeAsync("earth").then(console.log)
console.log("please wait...")
// "please wait..."
// { duration: 3614, result: "earth" }
// { duration: 4757, result: "hello" }

If you expect to set the end after getEditedData() is completed, you actually need to await getEditedData(). Otherwise, you'll go right past it while it executes... asynchrnously.

Related

What is the best way to handle function interrupts in javascript

Basically I have two async functions. One of them is a simple 5 second timeout and the other is a complicated async function with multiple steps.
Here is an example
const delay = ms => new Promise(res => setTimeout(res, ms));
class Runner {
async start() {
let printStuff = async () => {
for(let i = 0 ; i < 50; i++){
console.log(i);
await delay(50);
}
}
let printLetters = new Promise(async function(resolve, reject) {
const length = Math.floor(Math.random() * 10)
//dont know how long this will take
for(let i = 0; i < length; i++){
await printStuff();
}
resolve('letters');
});
let timeout = new Promise(async function(resolve, reject) {
await delay(5000);
resolve('timeout')
});
const finished = await Promise.all([timeout, printLetters]);
if(finished === 'timeout'){
this.stop();
}
}
stop(){
//stop printing stuff instantly
}
}
const myRunner = new Runner();
myRunner.start();
//could call myRunner.stop(); if the user canceled it
The way I would implement this would add a global variable and include an if statement inside the for loop to check if the interrupt has been called but I am wondering if there is a better way to implement this. An issue with this solution would be it would print a few more numbers. I would have to add another check to the other for loop and this could get messy quickly.
Here is a simple demo that uses my own library.
import { CPromise } from "c-promise2";
const task = CPromise.promisify(function* () {
let printStuff = CPromise.promisify(function* () {
for (let i = 0; i < 10; i++) {
console.log(i);
yield CPromise.delay(100);
}
});
const length = Math.floor(Math.random() * 10) + 3;
//dont know how long this will take
for (let i = 0; i < length; i++) {
yield printStuff();
}
return "letters";
});
const promise = task()
.timeout(5000)
.then(
(result) => console.log(`Done: ${result}`),
(err) => console.warn(`Fail: ${err}`)
);
setTimeout(() => {
promise.cancel();
}, 2000);
Is this what you're after?
What changed:
Promise.all replaced with Promise.race
added isStopped prop which makes the "complicated async function with multiple steps" skip execution for the remaining steps. it doesn't kill it, though. Promises are not cancelable.
const delay = ms => new Promise(res => setTimeout(res, ms));
class Runner {
isStopped = false;
async start() {
const printStuff = async () => {
let i = 0;
while (!this.isStopped) {
console.log(i++);
await delay(50);
}
}
const printLetters = new Promise(
resolve => printStuff()
.then(() => resolve('letters'))
);
const timeout = new Promise(
resolve => delay(5000)
.then(() => resolve('timeout'))
);
const finished = await Promise.race([timeout, printLetters]);
console.log({ finished });
if (finished === 'timeout') {
this.stop();
}
}
stop() {
this.isStopped = true;
}
}
const myRunner = new Runner();
myRunner.start();
<button onclick="myRunner.stop()">stop</button>
Initial answer (left it in as the comments reference it, not what's above; and in case someone finds it useful in 2074):
Here's an example outlining what I was suggesting in the comment. run() below returns a race between a rejector happening after 1s and a fulfiller which resolves in random time between 0 and 2s.
const rejector = (timeout) => new Promise((resolve, reject) => {
setTimeout(reject, timeout, 'rejected')
});
class Runner {
run() {
return Promise.race([
rejector(1000),
new Promise((resolve, reject) => {
setTimeout(resolve, Math.random() * 2000, 'fulfilled')
})
])
}
}
const t0 = performance.now();
[...Array(6).fill()].forEach((_, key) => {
const runner = new Runner();
runner.run()
.then(r => console.log(`Proomise ${key} ${r} after ${performance.now() - t0}ms`))
.catch(err => console.log(`Promise ${key} ${err} after ${performance.now() - t0}ms`));
})
Note: initially I placed the rejector inside the class but (at least for the above example) I don't see why it should not stay outside (in a real case scenario, imported from a helper file).
If you require an instantaneous stop capability, you would probably want to execute the print job as a external script. Then you use child processes like this.
const { spawn } = require('child_process');
class Runner {
......
start() {
this.job[somejobId] = spawn('command to execute script');
//this can be anything, including a node script, e.g. `node start.js`
.....
}
stop(jobId) {
if (jobId) {
//this will kill the script that you spawn above
this.job[jobId].kill('SIGHUP');
}
}
stopAllJobs() {
// looping through the job queue to kill all the jobs
this.job.forEach(job => job.kill('SIGHUP'))
}
}
You will have more info on how to start a child process from the node doc website https://nodejs.org/api/child_process.html#subprocesskillsignal
If your job (external script) is stalling, it's recommended that you only use the above codes if you have a minimum 2 CPU core, else it will affect your main process if your script is heavy.

Pause console output using Node.js

I am trying to create a time display (h:mm:ss) with just using the console and not on a web page. The time should be set initially and from there display the time as it changes.
When using the output console (in the web browser) or in vscode, is it possible for js or node.js, while printing thousands/multiple outputs to have a delay between each output being printed in the console without using an extension?
Here's an example of printing the current time to the console every 1 second:
// setInterval() executes the given function on a given time interval
setInterval(() => {
console.log(new Date().toTimeString()); // function to execute
}, 1000) // execute every 1000 ms
You can also print your own counter:
let secondsElapsed = 0
setInterval(() => {
console.log(`seconds elapsed = ${secondsElapsed}`);
secondsElapsed++;
}, 1000)
Here is how to accomplish what you seem to be asking:
const clockTick = (callback) => {
const tick = () => {
const now = Date.now();
callback(now);
setTimeout(tick, 1000 - (now % 1000));
}
tick();
}
clockTick(timestamp => {
const date = new Date(timestamp);
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
console.log(`${hours}:${minutes}:${seconds}`);
});
The accepted answer isn't correct. IMO, we should implement a queue or something similar in order to achieve "delay between each output". I find this question interesting so I implement this code for some fun (which use promise chain as a simple queue):
const callNext = (function () {
let queueChain = Promise.resolve();
// Called after previous command (1 second by default)
function callNext(func, delay = 1000) {
queueChain = queueChain.then(() => {
return new Promise((res) => {
setTimeout(() => {
func();
res();
}, delay);
});
});
}
return callNext;
})();
callNext(() => {
console.log('run after 3s')
}, 3000);
callNext(() => {
console.log('run after the above 3s')
}, 3000);
callNext(() => {
console.log('run after the above 1s by default')
});
we can create a simple logger using the above callNext for cleaner syntax:
const logger = (function () {
function createLogFn(name) {
return function (...args) {
let delay = 1000;
// take the last argument as "delay" param
if (typeof args[args.length -1] === 'number') {
delay = args.pop();
}
callNext(() => {
console[name](`${name.toUpperCase()} [${(new Date().toISOString())}]: `, ...args);
}, delay);
};
}
const logger = {
log: createLogFn('log'),
info: createLogFn('info'),
debug: createLogFn('debug'),
error: createLogFn('error'),
}
return logger;
})();
logger.log('run after 3s', 3000);
logger.log('run after the above 3s', 3000);
logger.log('run after the above 1s');
logger.info('final run after the above 2s', 2000);
Check it out: https://jsfiddle.net/pk5oqfxa/

closure example using setTimeout not recognizing function call

I am trying to create a small application that closes over the delay function and defines a new function using a callback and a wait time. I then wanted to use this newly created inner setTimeout function to take a single parameter that would be run on the callback after the wait time.
function addTwo(num) {
return num + 2
}
const delay = (callback, wait) => {
return setTimeout((value) => callback(value), wait)
}
var delayAddByTwo = delay(addTwo, 100)
console.log(delayAddByTwo(6))
// expected result 8
// actual result --- Uncaught TypeError: delayAddByTwo is not a function
As far as I can tell after the delayAddByTwo = delay(addTwo, 100) the only parameter left to create is the value passed to callback inside the 'inner' setTimeOut function. What am I missing about closure in this example?
You need to replace var delayAddByTwo = delay(addTwo, 100) by var delayAddByTwo = (num) => { delay(() => {addTwo(num)}, 100);}
function addTwo(num) {
console.log(num + 2)
return num + 2
}
const delay = (callback, wait) => {
setTimeout(callback, wait)
}
var delayAddByTwo = (num) => {
delay(() => {addTwo(num)}, 100);
}
console.log(delayAddByTwo)
delayAddByTwo(6)
// expected result 8
// actual result --- Uncaught TypeError: delayAddByTwo is not a function
You need to make delay return another function which accepts n. This n represents the number which will be passed into addTwo (ie the callback). setTimeout by default will return its timeoutID, so, instead, you can return a Promise, which resolves to the result of calling addTwo with n. In order to get your result from the Promise, you can await it within an async function.
See example below:
const addTwo = num => num + 2;
const delay = (callback, wait) => n => new Promise(res => setTimeout(res, wait, callback(n)));
(async () => {
const delayAddByTwo = delay(addTwo, 1000);
const res = await delayAddByTwo(6);
console.log(res);
})();
Above I have used the third argument of setTimeout, which will be used as the first argument of the callback and passed to res.
Change
var delayAddByTwo = delay(addTwo, 100)
To
const delayAddByTwo = delay => (addTwo, 100)

Can a javascript async method return multiple values at different times?

I have a list of values that I wanna return asynchronously, but a limited number of values at a time.
So I call my function once and it has an array of say 20 distinct elements say [1,2,...,20], and it keeps on returning 5 elements every second. So I want:
[1,2,..,5] at 0 sec,
[6,7,..,10] at 1 sec,
and so on....
I was thinking on using buffer files. If it is a valid solution, can someone tell me how to implement it for the given example?
An async generator function (or method) can yield (loosely, return) a sequence of values over time. Here's an example:
function sleepRandom(maxMs) {
return new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * maxMs)));
}
async function *example(max) {
yield -1; // Example of explicit value
// Wait 500ms
await sleepRandom(800);
// Example of loop
for (let i = 0; i < max; ++i) {
// Wait 500ms
await sleepRandom(800);
// Yield current loop value
yield i;
}
// All done
yield "Done";
}
(async () => {
for await (let value of example(10)) {
console.log(value);
}
})()
.catch(error => {
console.error(error);
});
The * after function is what makes it a generator function. The async before function is what makes it async.
You could use nodes EventEmitter for that:
var events = require('events');
function spitChunks(arr, chunksize = 5, delay = 1000, em = null) {
if(!em) em = new events.EventEmitter();
if(arr.length > 0) setTimeout(() => {
em.emit('nextChunk', arr.splice(0,chunksize));
spitChunks(arr, chunksize, delay, em);
}, delay);
return em;
}
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
let emitter = spitChunks(numbers);
emitter.on('nextChunk', data => console.log(data));

Sleep in Node.js

Assumed that there is no "native" way to achieve this, my solution-like was
sleep = function(time) {
var stop = new Date().getTime();
while(new Date().getTime() < stop + time) {
;
}
return new Promise((r,_)=> r())
}
So doing sleep(1000*3).then(()=>console.log("awake")) it will sleep 3 seconds and then resolve the Promise:
(be aware that it will freeze this page one sec.)
sleep = function(time) {
var stop = new Date().getTime();
while (new Date().getTime() < stop + time) {;
}
return new Promise((r, _) => r())
}
console.log("sleeping...")
sleep(1000 * 1).then(() => console.log("awake"))
Assumed that this will run in the main thread it will freeze the main process so that doing
sleep(1000*1).then(()=>console.log("awake")); console.log("Hello")
it will result in a output
VM2628:1 Hello
VM2628:1 awake
at very end of the sleep. Of course doing
setTimeout(()=>sleep(1000*3).then(()=>console.log("awake")),1000);console.log("Hello")
VM2815:1 Hello
undefined
VM2815:1 awake
will make it async, but it does not address my need (to put to sleep my main process).
Any better way?
[UPDATE]
Promisified version
/**
* Sleep for time [msec]
* #param time int milliseconds
* #return Promise delayed resolve
* #usage
sleep(1000*3).then(()=>console.log("awake"))
*/
sleepP: function (time) {
return new Promise((resolve, reject) => {
var stop = new Date().getTime();
while (new Date().getTime() < stop + time) {
;
}
return resolve(true)
});
}
that can be called like
await sleepP( 1000 * 3 );
There is no need to freeze at all. Because of javascripts asynchronicity we can leave a part of the code for some time and resume later. At first we need a promising timer:
const timer = ms => new Promise( res => setTimeout(res, ms));
Then we can simply use it:
console.log("wait 3 seconds")
timer(3000).then(_=>console.log("done"));
Or with a bit syntactic sugar:
(async function(){
console.log("wait 3 seconds");
await timer(3000);
console.log("done");
})()
If you really want to freeze ( very bad ), you don't need promises at all:
function freeze(time) {
const stop = new Date().getTime() + time;
while(new Date().getTime() < stop);
}
console.log("freeze 3s");
freeze(3000);
console.log("done");
Since Node v15.0.0 there's a new way to sleep by using the Timers Promises API.
import {setTimeout} from 'timers/promises';
const bucket = ['a', 'b', 'c'];
for(const item of bucket) {
await getItem(item);
await setTimeout(100);
}
If you want to use also use the regular setTimeout timer you can alias the promise timer.
import {setTimeout as sleep} from 'timers/promises';
// promise `setTimeout` aliased as `sleep`
sleep(100).then(() => console.log('I waited'));
// regular callback `setTimeout` (scheduling timers)
setTimeout(() => console.log('I also waited'), 100);
function sleep(time, func){
if (typeof func === 'function'){
const timer = ms => new Promise( res => setTimeout(res, ms));
timer(time).then(i=>func());
}
else{
console.log('What about the function bro?')
}
}
sleep(1000, function(){
console.log('hello')
console.log('test')
var arr = [1,2,3,4]
arr.forEach(i => console.log(i))
})

Categories