The best way to do a Callback after For in NodeJS? - node.js

I have been developing in PHP for many years and now I´m trying to learn NodeJS, and I´m having problems with asynchronous functions...
I have this function in NodeJS (source is an array of users):
//userformat.js
var age = module.exports.age = function(date){
var diff = new Date - new Date(date);
var diffdays = diff / 1000 / (60 * 60 * 24);
var age = Math.floor(diffdays / 365.25);
return age;
}
...etc.
//index.js
function format(source, me, callback){
var len = source.length
for (var i = 0; i < len; i++){
source[i]['_age'] = userFormat.age(source[i]['birthday']);
source[i]['_rating'] = userFormat.rating(source[i]['votes']);
source[i]['_is_followed'] = userFormat.followed(source[i]['fllws'], me);
}
callback(null, source);
}
I know that function is wrong in Node, because is possible that the function calls the callback before the functions in the for() had finished, and the values in the array could be undefined, for this reason I changed the function using callbacks:
//userformat.js
var age = module.exports.age = function(date, callback){
var diff = new Date - new Date(date);
var diffdays = diff / 1000 / (60 * 60 * 24);
var age = Math.floor(diffdays / 365.25);
callback(age);
}
...etc.
//index.js
function format(source, me, callback){
var len = source.length
for (var i = 0; i < len; i++){
userFormat.age(source[i]['birthday'], function(resul){
source[i]['_age'] = resul;
userFormat.rating(source[i]['votes'], function(resul){
source[i]['_rating'] = resul;
userFormat.followed(source[i]['fllws'], me, function(resul){
source[i]['_is_followed'] = resul;
//Callback
if (i == len-1){
callback(null, source);
}
})
})
})
}
}
That is correct? I have another way to do it more efficient or elegant?
Thank you so much!

I think you misunderstood the concept of asynchronous in nodeJS. Your code isn't asynchronous and your callback will always be called after the loop.
Check this question : how do i create a non-blocking asynchronous function in node.js?

Related

Variable is undefined outside of the function

Can someone please help me out with this? I'm getting an error 'count is not defined' when executing the below code. I'm trying to create a countdown clock in node.js. If I log the timer variable inside the function it works but outside of the function, it is undefined.
const getOrder = async(request, response) => {
const data = await knex("orderbook_table").select()
const time_data = await knex("orderbook_table").select("timestamp")
console.log(time_data)
var timer;
setInterval(function count() {
for (var i = 0; i < time_data.length; i++) {
//console.log(tableRows[i]);
var entryDate = time_data[i].timestamp;
const second = 1000,
minute = second * 60,
hour = minute * 60,
day = hour * 24;
countDown = new Date(entryDate).getTime();
let now = new Date().getTime();
distance = countDown - now;
days = Math.floor(distance / (day));
hours = Math.floor((distance % (day)) / (hour));
minutes = Math.floor((distance % (hour)) / (minute));
seconds = Math.floor((distance % (minute)) / second);
timer = days + "d:" + hours + "h:" + minutes + "m:" + seconds + "s";
//console.log(timer);
}
}, 1000);
var orderStr = data;
count();
console.log(timer);
response.send({ orderStr });
//refresh()
The count function is defined and scoped under the setTimer, you should create the function as callback outside of setInterval then call within that setInterval the created function
You can give a look to this previously answered thread or to the documentation

Buffer.swap16 is not a function

I've tested some buffer swap function in NodeJS, but in my Meteor project it doesn't work.
Can somebody tell me the reason?
Nodejs:
var CRC = new Buffer([0x20,0x40]);
console.log(CRC.swap16()); // OUTPUT: <Buffer 40 20>
Meteor.js
var CRC = new Buffer([0x20,0x40]);
console.log(CRC.swap16()); // OUTPUT: TypeError: CRC.swap16 is not a function
Can somebody help me please. In know it's maybe a version problem. but i can't solve it at the moment.
Is there also a way to get this thing running with meteor?
Buffer.swap16() method has been added in 5.10.0, whereas Meteor uses NodeJS version 4.
You can copy this method implementation from the NodeJS source, it is quite simple (with small modifications):
function swap(b, n, m) {
const i = b[n];
b[n] = b[m];
b[m] = i;
}
Buffer.prototype.swap16 = function swap16() {
const len = this.length;
if (len % 2 !== 0) {
throw new RangeError('ERR_INVALID_BUFFER_SIZE', '16-bits');
}
for (var i = 0; i < len; i += 2) {
swap(this, i, i + 1);
}
return this;
};

Calculate the bounding box of STL file with JavaScript

So I am using this npm package: node-stl
And its working great. However the regexp syntax, mathematics and geometrical calculations are somewhat confusing to me. Especially all at the same time.
Basically what I want to achieve is to extend the script to calculate the bounding box of the STL.
Here is the main file that calculates the volume and weight of the STL being parsed/read.
var fs = require('fs');
// Vertex
function Vertex (v1,v2,v3) {
this.v1 = Number(v1);
this.v2 = Number(v2);
this.v3 = Number(v3);
}
// Vertex Holder
function VertexHolder (vertex1,vertex2,vertex3) {
this.vert1 = vertex1;
this.vert2 = vertex2;
this.vert3 = vertex3;
}
// transforming a Node.js Buffer into a V8 array buffer
function _toArrayBuffer (buffer) {
var
ab = new ArrayBuffer(buffer.length),
view = new Uint8Array(ab);
for (var i = 0; i < buffer.length; ++i) {
view[i] = buffer[i];
}
return ab;
}
// calculation of the triangle volume
// source: http://stackoverflow.com/questions/6518404/how-do-i-calculate-the-volume-of-an-object-stored-in-stl-files
function _triangleVolume (vertexHolder) {
var
v321 = Number(vertexHolder.vert3.v1 * vertexHolder.vert2.v2 * vertexHolder.vert1.v3),
v231 = Number(vertexHolder.vert2.v1 * vertexHolder.vert3.v2 * vertexHolder.vert1.v3),
v312 = Number(vertexHolder.vert3.v1 * vertexHolder.vert1.v2 * vertexHolder.vert2.v3),
v132 = Number(vertexHolder.vert1.v1 * vertexHolder.vert3.v2 * vertexHolder.vert2.v3),
v213 = Number(vertexHolder.vert2.v1 * vertexHolder.vert1.v2 * vertexHolder.vert3.v3),
v123 = Number(vertexHolder.vert1.v1 * vertexHolder.vert2.v2 * vertexHolder.vert3.v3);
return Number(1.0/6.0)*(-v321 + v231 + v312 - v132 - v213 + v123);
}
// parsing an STL ASCII string
function _parseSTLString (stl) {
var totalVol = 0;
// yes, this is the regular expression, matching the vertexes
// it was kind of tricky but it is fast and does the job
var vertexes = stl.match(/facet\s+normal\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+outer\s+loop\s+vertex\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+vertex\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+vertex\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+endloop\s+endfacet/g);
vertexes.forEach(function (vert) {
var preVertexHolder = new VertexHolder();
vert.match(/vertex\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s/g).forEach(function (vertex, i) {
var tempVertex = vertex.replace('vertex', '').match(/[-+]?[0-9]*\.?[0-9]+/g);
var preVertex = new Vertex(tempVertex[0],tempVertex[1],tempVertex[2]);
preVertexHolder['vert'+(i+1)] = preVertex;
});
var partVolume = _triangleVolume(preVertexHolder);
totalVol += Number(partVolume);
})
var volumeTotal = Math.abs(totalVol)/1000;
return {
volume: volumeTotal, // cubic cm
weight: volumeTotal * 1.04 // gm
}
}
// parsing an STL Binary File
// (borrowed some code from here: https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/STLLoader.js)
function _parseSTLBinary (buf) {
buf = _toArrayBuffer(buf);
var
headerLength = 80,
dataOffset = 84,
faceLength = 12*4 + 2,
le = true; // is little-endian
var
dvTriangleCount = new DataView(buf, headerLength, 4),
numTriangles = dvTriangleCount.getUint32(0, le),
totalVol = 0;
for (var i = 0; i < numTriangles; i++) {
var
dv = new DataView(buf, dataOffset + i*faceLength, faceLength),
normal = new Vertex(dv.getFloat32(0, le), dv.getFloat32(4, le), dv.getFloat32(8, le)),
vertHolder = new VertexHolder();
for(var v = 3; v < 12; v+=3) {
var vert = new Vertex(dv.getFloat32(v*4, le), dv.getFloat32((v+1)*4, le), dv.getFloat32( (v+2)*4, le ) );
vertHolder['vert'+(v/3)] = vert;
}
totalVol += _triangleVolume(vertHolder);
}
var volumeTotal = Math.abs(totalVol)/1000;
return {
volume: volumeTotal, // cubic cm
weight: volumeTotal * 1.04 // gm
}
}
// NodeStl
// =======
// > var stl = NodeStl(__dirname + '/myCool.stl');
// > console.log(stl.volume + 'cm^3');
// > console.log(stl.weight + 'gm');
function NodeStl (stlPath) {
var
buf = fs.readFileSync(stlPath),
isAscii = true;
for (var i=0, len=buf.length; i<len; i++) {
if (buf[i] > 127) { isAscii=false; break; }
}
if (isAscii)
return _parseSTLString(buf.toString());
else
return _parseSTLBinary(buf);
}
module.exports = NodeStl;
If anyone could help me with this it would be great. I know and it feels like it simple. That I just need to know max/min of the different directions(x,y,z) and could then calculate the bounding box.
But I do not understand what the max/min for x,y and z is here. Please answer if you have an idea.
I've made a new branch https://github.com/johannesboyne/node-stl/tree/boundingbox could you please verify whether the applied algorithm works?
Best,
Johannes
Edit: If the branch is stable -> works I'll push it into v.0.1.0 (don't know why it is still 0.0.1)

How to get over 1000 records from a SuiteScript Saved Search?

Below is code I came up with to run a Saved Search in NetSuite using SuiteScript, create a CSV with the Saved Search results and then email the CSV. The trouble is, the results are limited to 1000 records. I've researched this issue and it appears the solution is to run a loop that slices in increments of 1000. A sample of what I believe is used to slice searches is also below.
However, I cannot seem to be able to incorporate the slicing into my code. Can anyone help me combine the slicing code with my original search code?
var search = nlapiSearchRecord('item', 'customsearch219729');
// Creating some array's that will be populated from the saved search results
var content = new Array();
var cells = new Array();
var temp = new Array();
var x = 0;
// Looping through the search Results
for (var i = 0; i < search.length; i++) {
var resultSet = search[i];
// Returns an array of column internal Ids
var columns = resultSet.getAllColumns();
// Looping through each column and assign it to the temp array
for (var y = 0; y <= columns.length; y++) {
temp[y] = resultSet.getValue(columns[y]);
}
// Taking the content of the temp array and assigning it to the Content Array.
content[x] += temp;
// Incrementing the index of the content array
x++;
}
//Inserting headers
content.splice(0, 0, "sku,qty,");
// Creating a string variable that will be used as the CSV Content
var contents;
// Looping through the content array and assigning it to the contents string variable.
for (var z = 0; z < content.length; z++) {
contents += content[z].replace('undefined', '') + '\n';
}
// Creating a csv file and passing the contents string variable.
var file = nlapiCreateFile('InventoryUpdate.csv', 'CSV', contents.replace('undefined', ''));
// Emailing the script.
function SendSSEmail()
{
nlapiSendEmail(768, 5, 'Inventory Update', 'Sending saved search via scheduled script', 'cc#email.com', null, null, file, true, null, 'cc#email.com');
}
The following code is an example of what I found that is used to return more than a 1000 records. Again, as a novice, I can't seem to incorporate the slicing into my original, functioning SuiteScript. Any help is of course greatly appreciated.
var filters = [...];
var columns = [...];
var results = [];
var savedsearch = nlapiCreateSearch( 'customrecord_mybigfatlist', filters, columns );
var resultset = savedsearch.runSearch();
var searchid = 0;
do {
var resultslice = resultset.getResults( searchid, searchid+1000 );
for (var rs in resultslice) {
results.push( resultslice[rs] );
searchid++;
}
} while (resultslice.length >= 1000);
return results;
Try out this one :
function returnCSVFile(){
function escapeCSV(val){
if(!val) return '';
if(!(/[",\s]/).test(val)) return val;
val = val.replace(/"/g, '""');
return '"'+ val + '"';
}
function makeHeader(firstLine){
var cols = firstLine.getAllColumns();
var hdr = [];
cols.forEach(function(c){
var lbl = c.getLabel(); // column must have a custom label to be included.
if(lbl){
hdr.push(escapeCSV(lbl));
}
});
return hdr.join(",");
}
function makeLine(srchRow){
var cols = srchRow.getAllColumns();
var line = [];
cols.forEach(function(c){
if(c.getLabel()){
line.push(escapeCSV(srchRow.getText(c) || srchRow.getValue(c)));
}
});
return line.join(",");
}
function getDLFileName(prefix){
function pad(v){ if(v >= 10) return v; return "0"+v;}
var now = new Date();
return prefix + '-'+ now.getFullYear() + pad(now.getMonth()+1)+ pad(now.getDate()) + pad( now.getHours()) +pad(now.getMinutes()) + ".csv";
}
var srchRows = getItems('item', 'customsearch219729'); //function that returns your saved search results
if(!srchRows) throw nlapiCreateError("SRCH_RESULT", "No results from search");
var fileLines = [makeHeader(srchRows[0])];
srchRows.forEach(function(soLine){
fileLines.push(makeLine(soLine));
});
var file = nlapiCreateFile('InventoryUpdate.csv', 'CSV', fileLines.join('\r\n'));
nlapiSendEmail(768, 5, 'Test csv Mail','csv', null, null, null, file);
}
function getItems(recordType, searchId) {
var savedSearch = nlapiLoadSearch(recordType, searchId);
var resultset = savedSearch.runSearch();
var returnSearchResults = [];
var searchid = 0;
do {
var resultslice = resultset.getResults(searchid, searchid + 1000);
for ( var rs in resultslice) {
returnSearchResults.push(resultslice[rs]);
searchid++;
}
} while (resultslice.length >= 1000);
return returnSearchResults;
}
I looked into your code but it seems you're missing the label headers in the generated CSV file. If you are bound to use your existing code then just replace
var search = nlapiSearchRecord('item', 'customsearch219729');
with
var search = getItems('item', 'customsearch219729');
and just use the mentioned helper function to get rid off the 1000 result limit.
Cheers!
I appreciate it has been a while since this was posted and replied to but for others looking for a more generic response to the original question the following code should suffice:
var search = nlapiLoadSearch('record_type', 'savedsearch_id');
var searchresults = search.runSearch();
var resultIndex = 0;
var resultStep = 1000;
var resultSet;
do {
resultSet = searchresults.getResults(resultIndex, resultIndex + resultStep); // retrieves all possible results up to the 1000 max returned
resultIndex = resultIndex + resultStep; // increment the starting point for the next batch of records
for(var i = 0; !!resultSet && i < resultSet.length; i++){ // loop through the search results
// Your code goes here to work on a the current resultSet (upto 1000 records per pass)
}
} while (resultSet.length > 0)
Also worth mentioning, if your code is going to be updating fields / records / creating records you need to bear in mind script governance.
Moving your code to a scheduled script to process large volumes of records is more efficient and allows you to handle governance.
The following line:
var savedsearch = nlapiCreateSearch( 'customrecord_mybigfatlist', filters, columns );
can be adapted to your own saved search like this:
var savedsearch = nlapiLoadSearch('item', 'customsearch219729');
Hope this helps.

Concurrency in Node.js using webworker-threads

This question is regarding the webworker-threads node.js module.
The documentation for the webworker-threads module is very limited and I can't find any newb-friendly examples of how to achieve my goal.
To put it simply, I have two CPU intensive functions that need to be run at the same time and must return a result and print it as soon as they finish. Because these synchronous methods are blocking, my current model is this:
What I'm trying to achieve is this:
However I'm no expert and I can't get my head around translating the information supplied in the Readme.md for the webworker-threads module into my own situation.
My current hack-job code is such:
var Worker = require('webworker-threads').Worker;
//console.time("worker1");
var worker = new Worker(function(){
onmessage = function(event) {
postMessage(event.data);
self.close();
};
});
worker.onmessage = function(event) {
console.log("Worker1 said : " + event.data);
};
worker.postMessage(work());
//console.timeEnd("worker1")
//console.time("worker2");
var worker2 = new Worker(function(){
onmessage = function(event) {
postMessage(event.data);
self.close();
};
});
worker2.onmessage = function(event) {
console.log("Worker2 said : " + event.data);
};
worker2.postMessage(work2());
//console.timeEnd("worker2")
function work(){
var total = 0;
for (var i=0; i<430046534; i++){
total += (i*i*i);
}
return total;
}
function work2(){
var total = 0;
for (var i=0; i<630746533; i++){
total += (i*i*i);
}
return total;
}
But alas, the results are only printed to the console when the second worker has finished, and I'm not even sure I'm using this module the right way.
You're posting the result of your work function to the worker. Put that function inside the web worker and then call it during onmessage.
var worker = new Worker(function(){
function work(n){
var res;
for (var i = 0; i < n; i++){
res += i * i * i;
}
return res;
}
onmessage = function(event) {
var result = work(event.data)
postMessage(result);
self.close();
};
});
You are doing all your heavy lifting on the server side, you want to put the work in the workers. You're blocking the entire server thread with having your work on the main thread.

Resources