NodeJS/Javascript - Using promises to process image collection then save aggregated outcome - javascript

Stock Overflow -
I'm trying to process an image collection (~2000 images) with NodeJS. I'm able to extract the information needed, but I'm having a hard time getting the timing right so that I can save the outcome to a JSON file.
Towards the end you'll see
console.log(palette);
// Push single image data to output array.
output.push(palette);
The console.log works fine, but the the push method is appears to be executed after the empty output array has been written to data.json. Tried having a nested promise where I wouldn't write the file until all images have been processed.
The callback function in getPixels gets executed asynchronously.
The order of the output array is arbitrary.
Any and all help greatly appreciated! Thank you!
// Extract color information from all images in imageDirectory
var convert = require('color-convert'),
fs = require('fs'),
getPixels = require("get-pixels"),
startTime = Date.now();
var processedImages = new Promise((resolve, reject) => {
var imageDirectory = 'input',
images = fs.readdirSync(imageDirectory),
output = [];
console.log('Found ' + images.length + ' images.');
for (var image in images) {
var imageLoaded = new Promise((resolve, reject) => {
getPixels(imageDirectory + '/' + images[image], function(error, pixels) {
if(error) {
return 'Bad image path';
}
resolve(pixels);
});
});
imageLoaded.then((pixels) => {
var palette = {
coloredPixels : 0,
hues : [],
image : images[image],
classification : false,
pixelCount : null
};
palette.pixelCount = pixels.shape[0] *
pixels.shape[1] *
pixels.shape[2];
for (var i = 0; i < 256; i++) {
palette.hues[i] = 0;
}
for (var i = 0; i < palette.pixelCount; i += 4) {
var rgb = [pixels.data[i ],
pixels.data[i + 1],
pixels.data[i + 2]],
hsl = convert.rgb.hsl(rgb),
hue = hsl[0],
saturation = hsl[1];
if (saturation) {
palette.hues[hue]++;
palette.coloredPixels++;
}
}
console.log(palette);
// Push single image data to output array.
output.push(palette);
});
}
resolve(output);
});
processedImages.then((output) => {
// write output array to data.json
var json = JSON.stringify(output, null, 2);
fs.writeFileSync('data.json', json);
// Calculate time spent
var endTime = Date.now();
console.log('Finished in ' + (endTime - startTime) / 1000 + ' seconds.');
});

What you want to do is transform an array of "images" to an array of promises and wait for all promises to resolve, and then perform more transformations. Think of it as a series of transformations, because that's what you're doing here. In a nutshell:
const imagePromises = images.map(image => new Promise(resolve, reject) {
getPixels(imageDirectory + '/' + image, (error, pixels) => {
if(error) {
reject('Bad image path');
return;
}
resolve(pixels);
});
const output = Promise.all(imagePromises).then(results =>
results.map(pixels => {
return {
// do your crazy palette stuff (build a palette object)
};
});

Related

How to make template for promise function with Sql queries in JavaScript?

I have few functions which does the same for different objects. For example:
const returnMiscArray = () => {
var i = 0;
var id = 0;
misc = _util.toNull(misc);
const promise = new Promise((resolve, reject) => {
while (Number(order.id[id]) != Number(orderid)) id++;
connection.query(`SELECT * FROM misc WHERE uniqid = ?`, [order.uniqid[id]], function(error, results) {
while (i < results.length) {
misc.uniqid[i] = results[i].uniqid;
misc.id[i] = results[i].id;
misc.count[i] = results[i].count;
misc.cost[i] = results[i].cost;
misc.product[i] = results[i].product;
misc.fcost[i] = results[i].fcost;
i++;
}
resolve(misc);
});
});
return (promise);
}
and
const returnPositionsArray = () => {
var i = 0;
var id = 0;
positions = _util.toNull(positions);
const promise = new Promise((resolve, reject) => {
while (Number(order.id[id]) != Number(orderid)) id++;
connection.query(`SELECT * FROM positions WHERE uniqid = ?`, [order.uniqid[id]], function(error, results) {
while (i < results.length) {
positions.uniqid[i] = results[i].uniqid;
positions.id[i] = results[i].id;
positions.length[i] = results[i].len;
positions.sqr[i] = results[i].sqr;
positions.cost[i] = results[i].cost;
positions.count[i] = results[i].count;
i++;
}
resolve(positions);
});
});
return (promise);
}
I want to make one function which takes object as argument, changes it and returns promise.
thinking of something like that:
const returnArray = (object, cur_order, id) => {
var i = 0;
var j = 0;
object = _util.toNull(object);
var objstr = somevalue; /* CONVERT PASSED OBJECT VARIABLE NAME TO STRING */
const promise = new Promise((resolve, reject) => {
while (Number(cur_order.id[j] != Number(id))) j++;
connection.query(`SELECT * FROM ` + objstr + ` WHERE uniqid = ?`, [cur_order.uniqid[j]], function(error, results) {
while (i < results.length) {
/*
SOMETHING HERE
*/
}
resolve(object);
});
return (promise);
});
}
but I have no idea how can i make it work. I know we can go through object parameters using keys, but how can I do the same for sql results?
Is there a way to make it work?
Ok so you have two functions which handle specific tables. You're already seeing that this is an opportunity to avoid repetition by using abstraction. The question is: what do we abstract?
Look at the two functions and see what is different between the two. Based on what you've written here, what you need to pass in is a table name and a callback that takes a result and it's index i.
But I don't love the code that you've written for a bunch of reasons.
It would probably be better to fetch multiple ids in one SQL call using IN with an array.
Your query callback needs to reject the Promise in the case of an error.
You’re resolving a promise from a loop?
I genuinely don't understand what you are doing with misc.uniqid[i]... Why not just return the results, or a mapped version of them?
Where are these variables order.id and orderid in the first two functions coming from? Can you write the function such that it gets everything it needs from its arguments?

Method from the pngjs library acts weirdly in Node.js

I am ordering images by a score value calculated by the getImageScore method, which is wrapped in a promise, as it takes quite some time to load the pixels it has to work with. I observed that the promises get blocked, never being finished. This was the initial whole program:
const fs = require('fs');
const { resolve } = require('path');
const { reject } = require('q');
const { Console } = require('console');
const gm = require('gm').subClass({imageMagick: true});
const PNG = require("pngjs").PNG;
let pathToFolder = '/home/eugen/Pictures/wallpapers1';
let pathToImage = '';
let promiseImageScore = new Promise((resolve, reject) => {
getImageScore(resolve, reject);
});
function getImageScore(resolve, reject) {
console.log('entered this promise....');
let img = gm(pathToImage);
// Get the PNG buffer
img.toBuffer("PNG", (err, buff) => {
if (err) return reject(err);
console.log('got buffer...');
// Get the image size
img.size((err, size) => {
if (err) {
console.log(err);
return reject(err);
}
console.log('got image size...');
// Parse the PNG buffer
let str = new PNG();
str.end(buff);
// After it's parsed...
str.on("parsed", buffer => {
// Get the pixels from the image
let idx, score = 0, rgb = {r: 0, g: 0, b: 0};
for (let y = 0; y < size.height; y++)
for (let x = 0; x < size.width; x++) {
idx = (size.width * y + x) << 2;
rgb.r = buffer[idx];
rgb.g = buffer[idx + 1];
rgb.b = buffer[idx + 2];
score += (rgb.r + rgb.g + rgb.b) / 765;
}
console.log('one promise finished...');
return resolve(score / (size.height * size.width));
});
str.on("error", e => {
return reject(e);
});
});
});
}
// see which images are to be found in the specificd directory
fs.readdir(pathToFolder, function (err, files) {
if (err) return console.log('Unable to scan directory: ' + err);
console.log('files in directory:\n');
files.forEach(function (file) {
pathToImage = pathToFolder + '/' + file;
//showImageScore();
promiseImageScore
.then(imageScore => {
console.log(file + ' has a score of ' + imageScore);
})
.catch(e => {
throw e;
})
});
});
Running the above code would result in this output:
entered this promise....
files in directory:
got buffer...
After logging the got buffer message, the program would just run continuously... I saw that, by modifying the way I'm appealing images, I would finally get the got image size log in the console. Therefore, here is the way I modified the getImageScore method:
function getImageScore(resolve, reject) {
console.log('entered this promise....');
//let img = gm(pathToImage);
// Get the PNG buffer
//img.toBuffer("PNG", (err, buff) => {
gm(pathToImage).toBuffer("PNG", (err, buff) => {
if (err) return reject(err);
console.log('got buffer...');
// Get the image size
//img.size((err, size) => {
gm(pathToImage).size((err, size) => {
if (err) {
console.log(err);
return reject(err);
}
console.log('got image size...');
// Parse the PNG buffer
let str = new PNG();
console.log('created str...');
str.end(buff);
console.log('got str...');
// After it's parsed...
str.on("parsed", buffer => {
// Get the pixels from the image
let idx, score = 0, rgb = {r: 0, g: 0, b: 0};
for (let y = 0; y < size.height; y++)
for (let x = 0; x < size.width; x++) {
idx = (size.width * y + x) << 2;
rgb.r = buffer[idx];
rgb.g = buffer[idx + 1];
rgb.b = buffer[idx + 2];
score += (rgb.r + rgb.g + rgb.b) / 765;
}
console.log('one promised finished...');
return resolve(score / (size.height * size.width));
});
str.on("error", e => {
return reject(e);
});
});
});
}
After making these changes, I am getting the following output in the console:
entered this promise....
files in directory:
got buffer...
got image size...
created str...
events.js:174
throw er; // Unhandled 'error' event
^
Error: Invalid file signature
at module.exports.Parser._parseSignature (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/parser.js:53:18)
at module.exports.ChunkStream._processRead (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:174:13)
at module.exports.ChunkStream._process (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:193:14)
at module.exports.ChunkStream.write (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:61:8)
at module.exports.ChunkStream.end (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:74:10)
at exports.PNG.PNG.end (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/png.js:98:16)
at gm.size (/home/eugen/Documents/scripts/sort_pictures_by_brightness/index.js:34:11)
at gm.emit (events.js:198:13)
at gm.<anonymous> (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/gm/lib/getters.js:82:14)
at cb (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/gm/lib/command.js:322:16)
Emitted 'error' event at:
at module.exports.emit (events.js:198:13)
at module.exports.ChunkStream._process (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:207:10)
at module.exports.ChunkStream.write (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/pngjs/lib/chunkstream.js:61:8)
[... lines matching original stack trace ...]
at cb (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/gm/lib/command.js:322:16)
at ChildProcess.onExit (/home/eugen/Documents/scripts/sort_pictures_by_brightness/node_modules/gm/lib/command.js:305:9)
From seeing the output, we can say that the str.end(buff); line has some kind of problem, as the program never outputs the got str log. This problem didn't seem to exist, before making the changes I made in the getImageScore method. Firstly, I don't really understand why loading an image in a local object would be a problem that would cause the code to act unexpectedly. Secondly, modifying the way an image is loaded should not alter the end method from the pngjs library. Can someone explain what is actually happening here and how can this issue be fixed?
Fortunately, someone from Facebook (Iulian Popescu) told me that files.forEach doesn't wait for the promises to finish, therefore they get blocked. I managed to solve this situation, by adding more promises. For sample, one of the promises reads all the data. After reading all the data and saving it in some global variables, the other promises would get executed, in the right order. If you'd like to actually see the implementation of the solution I'm talking about, you can check it out on my GitHub: https://github.com/tomaAlex/darkImageClassifier/blob/master/index.js

Using data i get from request function in node.JS again until a condition is met

I want to access shopify api using Node.js with request method. I get first 50 items but i need to send the last id of the products i get as a response so it can loop through all the products until we don't have another id (i check that if the last array is not 50 in length.)
So when i get the response of lastID i want to feed that again to the same function until the Parraylength is not 50 or not 0.
Thing is request works asynchronously and i don't know how to feed the same function with the result lastID in node.js.
Here is my code
let importedData = JSON.parse(body);
//for ( const product in importedData.products ){
// console.log(`${importedData.products[product].id}`);
//}
lastID = importedData.products[importedData.products.length-1].id;
let lastIDD = lastID;
console.log(`This is ${lastID}`);
importedData ? console.log('true') : console.log('false');
let Prarraylength = importedData.products.length;
console.log(Prarraylength);
//console.log(JSON.stringify(req.headers));
return lastIDD;
});```
You can use a for loop and await to control the flow of your script in this case.
I'd suggest using the request-native-promise module to get items, since it has a promise based interface, but you could use node-fetch or axios (or any other http client) too.
In this case, to show you the logic, I've created a mock rp which normally you'd create as follows:
const rp = require("request-promise-native");
You can see we're looping through the items, 50 at a time. We're passing the last id as a url parameter to the next rp call. Now this is obviously going to be different in reality, but I believe you can easily change the logic as you require.
const totalItems = 155;
const itemsPerCall = 50;
// Mock items array...
const items = Array.from({ length: totalItems}, (v,n) => { return { id: n+1, name: `item #${n+1}` } });
// Mock of request-promise (to show logic..)
// Replace with const rp = require("request-promise-native");
const rp = function(url) {
let itemPointer = parseInt(url.split("/").slice(-1)[0]);
return new Promise((resolve, reject) => {
setTimeout(() => {
let slice = items.slice(itemPointer, itemPointer + itemsPerCall);
itemPointer += itemsPerCall;
resolve( { products: slice });
}, 500);
})
}
async function getMultipleRequests() {
let callIndex = 0;
let lastID = 0;
const MAX_CALLS = 20;
const EXPECTED_ARRAY_LENGTH = 50;
for(let callCount = 1; callCount < MAX_CALLS; callCount++) {
// Replace with the actual url..
let url = "/products/" + lastID;
let importedData = await rp(url);
lastID = importedData.products[importedData.products.length - 1].id;
console.log("Call #: " + ++callIndex + ", Item count: " + importedData.products.length + ", lastID: " + lastID);
if (importedData.products.length < EXPECTED_ARRAY_LENGTH) {
console.log("Reached the end of products...exiting loop...");
break;
}
}
}
getMultipleRequests();

once("child_added") on non existing node does not fail but timeout

First I'm not sure that there is a real problem but I guess I'll share my reasoning.
I use Firebase as a database / backend for the archiving of all the data from various sensors at home and an UI with cool graphs in hosting. So every 10 minutes I push various data (temperature, humidity, CO2 level, illumination, ...) coming from various rooms. I have almost 3 years of data available (so my base has a lots of nodes)
So my database structure is like that :
root
readings
room_id
GUID
time
temp
hum
lum
For a few years I had a PHP script hosted at home that checked if the latest item inside each readings/room_id has a time value that is not too old (no more than 11 minutes old). I translated it to Firebase cloud function some days ago and I got something like this :
exports.monitor = functions.https.onRequest((req, res) => {
const tstamp = Math.floor(Date.now() / 1000);
var sensors = ["r01", "r02", "r03", "r04", "r05"];
var promiseArray = [];
var result = {};
for (var i = 0; i < sensors.length; i++) {
console.log('Adding promise for ' + sensors[i]);
promiseArray.push(admin.database().ref('/readings/' + sensors[i]).limitToLast(1).once("child_added"));
}
Promise.all(promiseArray).then(snapshots => {
console.log('All promises done : ' + snapshots.length);
res.set('Cache-Control', 'private, max-age=300');
for (var i = 0; i < snapshots.length; i++) {
differenceInMinutes = (tstamp - snapshots[i].val().time) / 60;
result[sensors[i]] = {current: tstamp,
sensor: snapshots[i].val().time,
diff: Math.round(differenceInMinutes * 10) / 10};
if (differenceInMinutes < 11) {
result[sensors[i]]['status'] = "OK";
} else {
result[sensors[i]]['status'] = "KO";
}
}
return res.status(200).json(result);
}).catch(error => {
console.error('Error while getting sensors details', error.message);
res.sendStatus(500);
});
});
The code works well. So my question is : if I add another room ID in the sensors array that does not exists inside "readings" in my database, I thought I'll get an error (failed promise) instead I only got a huge timeout error, I don't want that kind of timeout on Firebase Cloud Functions (to avoid any unwanted cost).
Is that normal ? Is my code wrong ? Do I have to start by getting a shallow snapshot of "readings/room_id" check that it exists and check if has children ?
Thanks a lot for your help.
EDIT : With the help of Frank I fixed my code, here is the revised version :
exports.monitor = functions.https.onRequest((req, res) => {
const tstamp = Math.floor(Date.now() / 1000);
var sensors = ["r01", "r02", "r03", "r04", "r05"];
var promiseArray = [];
var result = {};
for (var i = 0; i < sensors.length; i++) {
console.log('Adding promise for ' + sensors[i]);
promiseArray.push(admin.database().ref('/readings/' + sensors[i]).limitToLast(1).once("value"));
}
Promise.all(promiseArray).then(queryResults => {
console.log('All promises done : ' + queryResults.length);
res.set('Cache-Control', 'private, max-age=300');
queryResults.forEach((snapshots, i) => {
snapshots.forEach((snapshot) => {
var currentData = snapshot.val();
differenceInMinutes = (tstamp - currentData.time) / 60;
result[sensors[i]] = {current: tstamp,
sensor: currentData.time,
diff: Math.round(differenceInMinutes * 10) / 10};
if (differenceInMinutes < 11) {
result[sensors[i]]['status'] = "OK";
} else {
result[sensors[i]]['status'] = "KO";
}
});
});
return res.status(200).json(result);
}).catch(error => {
console.error('Error while getting sensors details', error.message);
res.sendStatus(500);
});
});
a child_added event only fires when there is a child node. If there are not child nodes under the location (or matching the query) it will not fire.
To ensure you also get notified in the condition there are no children, you should listen to the value event:
for (var i = 0; i < sensors.length; i++) {
console.log('Adding promise for ' + sensors[i]);
var query = admin.database().ref('/readings/' + sensors[i]).limitToLast(1).once("value")
promiseArray.push(query);
}
Since a value event may match multiple children in a single snapshot (despite your query only requesting a single child), you will need to loop over the children of the resulting snapshot:
Promise.all(promiseArray).then((queryResults) => {
console.log('All promises done : ' + queryResults.length);
res.set('Cache-Control', 'private, max-age=300');
queryResults.forEach((snapshots) => {
snapshots.forEach((snapshot) => {
differenceInMinutes = (tstamp - snapshot.val().time) / 60;
...

How do I measure the execution time of JavaScript code with callbacks?

I have a piece of JavaScript code that I am executing using the node.js interpreter.
for(var i = 1; i < LIMIT; i++) {
var user = {
id: i,
name: "MongoUser [" + i + "]"
};
db.users.save(user, function(err, saved) {
if(err || !saved) {
console.log("Error");
} else {
console.log("Saved");
}
});
}
How can I measure the time taken by these database insert operations? I could compute the difference of date values after and before this piece of code but that would be incorrect because of the asynchronous nature of the code.
Use the Node.js console.time() and console.timeEnd():
var i;
console.time("dbsave");
for(i = 1; i < LIMIT; i++){
db.users.save({id : i, name : "MongoUser [" + i + "]"}, end);
}
end = function(err, saved) {
console.log(( err || !saved )?"Error":"Saved");
if(--i === 1){
console.timeEnd("dbsave");
}
};
There is a method that is designed for this. Check out process.hrtime(); .
So, I basically put this at the top of my app.
var start = process.hrtime();
var elapsed_time = function(note){
var precision = 3; // 3 decimal places
var elapsed = process.hrtime(start)[1] / 1000000; // divide by a million to get nano to milli
console.log(process.hrtime(start)[0] + " s, " + elapsed.toFixed(precision) + " ms - " + note); // print message + time
start = process.hrtime(); // reset the timer
}
Then I use it to see how long functions take. Here's a basic example that prints the contents of a text file called "output.txt":
var debug = true;
http.createServer(function(request, response) {
if(debug) console.log("----------------------------------");
if(debug) elapsed_time("recieved request");
var send_html = function(err, contents) {
if(debug) elapsed_time("start send_html()");
response.writeHead(200, {'Content-Type': 'text/html' } );
response.end(contents);
if(debug) elapsed_time("end send_html()");
}
if(debug) elapsed_time("start readFile()");
fs.readFile('output.txt', send_html);
if(debug) elapsed_time("end readFile()");
}).listen(8080);
Here's a quick test you can run in a terminal (BASH shell):
for i in {1..100}; do echo $i; curl http://localhost:8080/; done
Invoking console.time('label') will record the current time in milliseconds, then later calling console.timeEnd('label') will display the duration from that point.
The time in milliseconds will be automatically printed alongside the label, so you don't have to make a separate call to console.log to print a label:
console.time('test');
//some code
console.timeEnd('test'); //Prints something like that-> test: 11374.004ms
For more information, see Mozilla's developer docs on console.time.
Surprised no one had mentioned yet the new built in libraries:
Available in Node >= 8.5, and should be in Modern Browers
https://developer.mozilla.org/en-US/docs/Web/API/Performance
https://nodejs.org/docs/latest-v8.x/api/perf_hooks.html#
Node 8.5 ~ 9.x (Firefox, Chrome)
// const { performance } = require('perf_hooks'); // enable for node
const delay = time => new Promise(res=>setTimeout(res,time))
async function doSomeLongRunningProcess(){
await delay(1000);
}
performance.mark('A');
(async ()=>{
await doSomeLongRunningProcess();
performance.mark('B');
performance.measure('A to B', 'A', 'B');
const measure = performance.getEntriesByName('A to B')[0];
// firefox appears to only show second precision.
console.log(measure.duration);
// apparently you should clean up...
performance.clearMarks();
performance.clearMeasures();
// Prints the number of milliseconds between Mark 'A' and Mark 'B'
})();
https://repl.it/#CodyGeisler/NodeJsPerformanceHooks
Node 12.x
https://nodejs.org/docs/latest-v12.x/api/perf_hooks.html
const { PerformanceObserver, performance } = require('perf_hooks');
const delay = time => new Promise(res => setTimeout(res, time))
async function doSomeLongRunningProcess() {
await delay(1000);
}
const obs = new PerformanceObserver((items) => {
console.log('PerformanceObserver A to B',items.getEntries()[0].duration);
// apparently you should clean up...
performance.clearMarks();
// performance.clearMeasures(); // Not a function in Node.js 12
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
(async function main(){
try{
await performance.timerify(doSomeLongRunningProcess)();
performance.mark('B');
performance.measure('A to B', 'A', 'B');
}catch(e){
console.log('main() error',e);
}
})();
For anyone want to get time elapsed value instead of console output :
use process.hrtime() as #D.Deriso suggestion, below is my simpler approach :
function functionToBeMeasured() {
var startTime = process.hrtime();
// do some task...
// ......
var elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime));
console.log('It takes ' + elapsedSeconds + 'seconds');
}
function parseHrtimeToSeconds(hrtime) {
var seconds = (hrtime[0] + (hrtime[1] / 1e9)).toFixed(3);
return seconds;
}
var start = +new Date();
var counter = 0;
for(var i = 1; i < LIMIT; i++){
++counter;
db.users.save({id : i, name : "MongoUser [" + i + "]"}, function(err, saved) {
if( err || !saved ) console.log("Error");
else console.log("Saved");
if (--counter === 0)
{
var end = +new Date();
console.log("all users saved in " + (end-start) + " milliseconds");
}
});
}
Old question but for a simple API and light-weight solution; you can use perfy which uses high-resolution real time (process.hrtime) internally.
var perfy = require('perfy');
function end(label) {
return function (err, saved) {
console.log(err ? 'Error' : 'Saved');
console.log( perfy.end(label).time ); // <——— result: seconds.milliseconds
};
}
for (var i = 1; i < LIMIT; i++) {
var label = 'db-save-' + i;
perfy.start(label); // <——— start and mark time
db.users.save({ id: i, name: 'MongoUser [' + i + ']' }, end(label));
}
Note that each time perfy.end(label) is called, that instance is auto-destroyed.
Disclosure: Wrote this module, inspired by D.Deriso's answer. Docs here.
You could also try exectimer. It gives you feedback like:
var t = require("exectimer");
var myFunction() {
var tick = new t.tick("myFunction");
tick.start();
// do some processing and end this tick
tick.stop();
}
// Display the results
console.log(t.timers.myFunction.duration()); // total duration of all ticks
console.log(t.timers.myFunction.min()); // minimal tick duration
console.log(t.timers.myFunction.max()); // maximal tick duration
console.log(t.timers.myFunction.mean()); // mean tick duration
console.log(t.timers.myFunction.median()); // median tick duration
[edit] There is an even simpler way now to use exectime. Your code could be wrapped like this:
var t = require('exectimer'),
Tick = t.Tick;
for(var i = 1; i < LIMIT; i++){
Tick.wrap(function saveUsers(done) {
db.users.save({id : i, name : "MongoUser [" + i + "]"}, function(err, saved) {
if( err || !saved ) console.log("Error");
else console.log("Saved");
done();
});
});
}
// Display the results
console.log(t.timers.myFunction.duration()); // total duration of all ticks
console.log(t.timers.saveUsers.min()); // minimal tick duration
console.log(t.timers.saveUsers.max()); // maximal tick duration
console.log(t.timers.saveUsers.mean()); // mean tick duration
console.log(t.timers.saveUsers.median()); // median tick duration
You can use a wrapper function to easily report the execution time of any existing function.
A wrapper is a used to extend an existing function to do something before and after the existing function's execution - and is a convenient way to compose logic.
Here is an example of using the withDurationReporting wrapper:
// without duration reporting
const doSomethingThatMayTakeAWhile = async (someArg: string, anotherArg: number) => {
/** your logic goes here */
}
// with duration reporting
const doSomethingThatMayTakeAWhileWithReporting = withDurationReporting(
'doSomethingThatMayTakeAWhile',
doSomethingThatMayTakeAWhile
);
// note: you can define the function with duration reporting directly, too
const doSomethingThatMayTakeAWhile = withDurationReporting(
'doSomethingThatMayTakeAWhile',
async (someArg: string, anotherArg: number) => {
/** your logic goes here */
}
)
And here is the wrapper itself:
import { hrtime } from 'process';
const roundToHundredths = (num: number) => Math.round(num * 100) / 100; // https://stackoverflow.com/a/14968691/3068233
/**
* a wrapper which reports how long it took to execute a function, after the function completes
*/
export const withDurationReporting = <R extends any, T extends (...args: any[]) => Promise<R>>(
title: string,
logic: T,
options: {
reportingThresholdSeconds: number;
logMethod: (message: string, metadata?: Record<string, any>) => void;
} = {
reportingThresholdSeconds: 1, // report on anything that takes more than 1 second, by default
logMethod: console.log, // log with `console.log` by default
},
) => {
return (async (...args: Parameters<T>): Promise<R> => {
const startTimeInNanoseconds = hrtime.bigint();
const result = await logic(...args);
const endTimeInNanoseconds = hrtime.bigint();
const durationInNanoseconds = endTimeInNanoseconds - startTimeInNanoseconds;
const durationInSeconds = roundToHundredths(Number(durationInNanoseconds) / 1e9); // https://stackoverflow.com/a/53970656/3068233
if (durationInSeconds >= options.reportingThresholdSeconds)
options.logMethod(`${title} took ${durationInSeconds} seconds to execute`, { title, durationInSeconds });
return result;
}) as T;
};
I designed a simple method for this, using console.time() & console.timeEnd():
measure function definition
function measureRunningTime(func,...args){
const varToString = varObj => Object.keys(varObj)[0]
const displayName = func.name || varToString({ func })
console.time(displayName)
func(...args)
console.timeEnd(displayName)
}
To use it, pass a function without arguments, with arguments binded, or with arguments as the following parameters.
Examples:
let's say I want to check the running time of the simplest searching algorithm - SimpleSearch:
measured function definition (your code here)
const simpleSearch = (array = [1,2,3] ,item = 3) => {
for(let i = 0; i< array.length; i++){
if (array[i] === item) return i;
}
return -1
}
implementation without arguments
measureRunningTime(simpleSearch)
//Prints something like that-> simpleSearch: 0.04ms
implementation with arguments using .bind()
const array = [1,2,3]
const item = 3
measureRunningTime(simpleSearch.bind(null, array, item))
//Prints something like that-> bound simpleSearch: 0.04ms
implementation with arguments without using .bind()
const array = [1,2,3]
const item = 3
measureRunningTime(simpleSearch, array, item)
//Prints something like that-> simpleSearch: 0.04ms
-> Take notice!! this implementation is far from perfect - for example there is no error handling - but it can be used to check the running times of simple algorithms,
Moreover , I'm not an experienced programmer so take everything with a grain of salt 🧂 👌
I had same issue while moving from AWS to Azure
For express & aws, you can already use, existing time() and timeEnd()
For Azure, use this:
https://github.com/manoharreddyporeddy/my-nodejs-notes/blob/master/performance_timers_helper_nodejs_azure_aws.js
These time() and timeEnd() use the existing hrtime() function, which give high-resolution real time.
Hope this helps.
I need this to be cumulative, and to measure different stuff.
Built these functions:
function startMeasuring(key) {
measureTimers[key] = process.hrtime();
}
function stopMeasuring(key) {
if (!measures[key]) {
measures[key] = 0;
}
let hrtime = process.hrtime(measureTimers[key]);
measures[key] += hrtime[0] + hrtime[1] / 1e9;
measureTimers[key] = null;
}
Usage:
startMeasuring("first Promise");
startMeasuring("first and second Promises");
await new Promise((resolve) => {
setTimeout(resolve, 1400);
});
stopMeasuring("first Promise");
stopMeasuring("first and second Promises");
startMeasuring("first and second Promises");
await new Promise((resolve) => {
setTimeout(resolve, 600);
});
stopMeasuring("first and second Promises");
console.log("Measure Results", measures);
/*
Measusre Results {
setting: 0.00002375,
'first Promise': 1.409392916,
'first and second Promise': 2.015160376
}
*/

Categories