For-loop and async callback in node.js? - node.js

I'm new to JavaScript and to node.js. I want to loop through a directory and add all file stat (not other directories) to an array. As you see below there is a problem with my code since the callback will probably get called after the for loop has finished so using the "i"-variable in the callback method will not work. But how should the code look so that the below snippet works? Does it have something to do with closures?
Thanks for help!
fs.readdir(SYNCDIR, function(err1, files) {
var filesOnly = [];
if(!err1) {
for(var i = 0; i < files.length; i++) {
var imgFilePath = SYNCDIR + '/' + files[i];
fs.stat(imgFilePath, function(stat){
if (stat.isFile()){
filesOnly[i] = stat; // This will not be correct since the for-loop has finished
}
});
}
}
});

You are right about needing to use a closure. You should wrap the contents of the for loop in a self-invoking function to preserve the value of i for each iteration.
fs.readdir(SYNCDIR, function(err1, files) {
var filesOnly = [];
if(!err1) {
for(var i = 0; i < files.length; i++) {
(function(i) {
var imgFilePath = SYNCDIR + '/' + files[i];
fs.stat(imgFilePath, function(stat){
if (stat.isFile()){
filesOnly[i] = stat;
}
});
})(i);
}
}
});

One way is to rewrite the innards of the loop to use a closure:
fs.readdir(SYNCDIR, function(err1, files) {
var filesOnly = [];
if(!err1) {
for(var i = 0; i < files.length; i++) {
(function(index) {
var imgFilePath = SYNCDIR + '/' + files[index];
fs.stat(imgFilePath, function(stat){
if (stat.isFile()){
filesOnly[index] = stat;
}
});
})(i);
}
}
});
A better looking example, achieving the same, using Array.prototype.forEach:
fs.readdir(SYNCDIR, function(err1, files) {
var filesOnly = [];
if(!err1) {
files.forEach(function(file, i) {
var imgFilePath = SYNCDIR + '/' + file;
fs.stat(imgFilePath, function(stat){
if (stat.isFile()){
filesOnly[i] = stat;
}
});
});
}
});

Alternatively use the new threads module ( https://github.com/robtweed/Q-Oper8 ) and then you can do all this stuff much more simply using standard synchronous coding within the threads child processes, since they only deal with one user's request at a time.
Goodbye to async logic and nested callbacks!

Related

Node.js for loop output only last item from JSON

The code below only output the last result, I don't get it. I check if the updateDate item contains 2020-05 both items does and I get only the last one. The loop is not looping :)
const briefing = [
{
"updateDate": "2020-05-05T00:00:00.0Z",
},
{
"updateDate": "2020-05-06T00:00:00.0Z",
},
{
"updateDate": "2020-05-13T00:00:00.0Z",
}
];
let date = new Date();
var formattedYearMonth = date.getFullYear() + '-' + ('0' + (date.getMonth()+1)).slice(-2) + '-';
for (var i = 0; i < briefing.length; i++) {
var jsonDate = briefing[i].updateDate;
if (jsonDate.includes(formattedYearMonth)) {
var response = JSON.stringify(briefing[i]);
}
}return response;
}
for (var i = 0; i < briefing.length; i++) {
var jsonDate = briefing[i].updateDate;
if (jsonDate.includes(formattedYearMonth)) {
var response = JSON.stringify(briefing[i]); // <==== THIS IS WHERE YOUR PROBLEM LIES
}
}return response;
The loop is actually looping :). But for every run of the loop, you are resetting the value of response.
--EDITED--
For the response to be an array, you need to modify your code as
let response = [];
for (var i = 0; i < briefing.length; i++) {
var jsonDate = briefing[i].updateDate;
if (jsonDate.includes(formattedYearMonth)) {
response.push(JSON.stringify(briefing[i]));
}
}
return response;

Accessing variables from my index.js in another file

I am trying to make a bit of code execute as if it was in the index.js of my nodejs app.
I have the following code:
index.js
var data = [];
function populateData(){
for(var i = 0; i < 100; i++){
data.push(i);
}
}
populateData();
require('./other.js')();
other.js
module.exports = function(){
console.log(data);
}
However, this tells me data is not defined. Is there any way to read the data variable like this?
I tried
require('./other.js').apply(null);
and
require('./other.js').apply(this);
However neither worked for me
Use another module for common variable.
common.js
module.exports = {
data: []
}
index.js
const common = require('./common');
const other = require('./other');
function populateData(){
for(var i = 0; i < 100; i++){
common.data.push(i);
}
}
populateData();
other.test();
other.js
const common = require("./common");
module.exports = {
test: function(){
console.log(common.data);
}
}
You can pass the data to the file like this
index.js
var data = [];
function populateData() {
for (var i = 0; i < 100; i++) {
data.push(i);
}
}
populateData();
require('./other.js')(data);
other.js
module.exports = function(data) {
console.log(data);
};

Synchronous http request in node js?

I'm looking for a simple way to perform synchronous http-requests in node.js, but it's still getting async responses ...
I've realised that node.js is recommended to async jobs, but in my case,
I need the synchronous response to call other functions that use this data, if it's null/undefined, I can't proceed with the flow...
What's the better way to do that?
Here's my code:
function callCellId(data) {
console.log("Data: " + data);
var towers = [],
rscp = [];
var request = require('sync-request');
for (var x = 0; x < data.length; x++) {
console.log("Request data: \n");
rscp[x] = data[x].rscp;
var res = request('POST', 'http://opencellid.org/cell/get?key=xxxxx&mcc=' + data[x].mcc + '&mnc=' + data[x].mnc + '&lac=' + data[x].LAC + '&cellid=' + data[x].cellID + '&format=json');
console.log("loop " + x);
data = res.getBody().toString();
console.log("rsp: " + data);
towers[x] = {
'latitude': data.lat,
'longitude': data.lon,
'rscp': rscp[x],
'signal': data.averageSignalStrength
};
}
console.log("Content for triangulation" + JSON.stringify(towers));
return towers;
}
Using async in a loop cloud be tricky.
I solved this without external libraries using generators:
LoopOperation: function() {
//define generator with the main loop
var loopIterator = function*() {
for (var index = 0; index < 10; index++) {
var result = yield asyncOperation( (res) => loopIterator.next(res)); //do something asyc and execute () => loopIterator.next() when done as callback
console.log(result);
}
}();
loopIterator.next(); //start loop
}
Since the nodejs nature is async, every time we need some sync call (like this nested request stack), we're able to use promises
"A Promise is an object representing the eventual completion or failure of an asynchronous operation"
reference
I.E:
const request = require('request-promise');
function callCellId(data) {
let towers = [];
let options = {
url: 'http://opencellid.org/cell/get',
method: 'POST',
json: true
};
data.forEach(location => {
options.body = {
key: 'YOUR_PRIVATE_KEY',
mcc: location.mcc,
mnc: location.mnc,
lac: location.lac,
cellId: location.cellID
}
request(options).then(cellInfo => {
towers.push({
latitude: cellInfo.lat,
longitude: cellInfo.lon,
rscp: location.rscp,
signal: cellInfo.averageSignalStrength
});
}).catch(err => {
console.log('Could not get cellId Info for',location);
console.log(err);
});
});
return towers;
}

show folders using node.js and handlebars.js

i want to show folders that i ahve aunder some directory,
im using express3-handlebars.
html code:
{{gallery}}
here what i have:
res.render('pages/gallery', {
title: 'גלריה',
HomeActive:'',
ContactActive:'',
AboutActive:'',
GalleryActive:'active',
MapActive:'',
EmailActive:'',
helpers:{
gallery:function(){
var albums = [],
albumDir = './public/img/albums';
eventEmitter.on('readFolder',function(){
var albums = [];
fs.readdir(albumDir, function (err, files) {
if (err) console.log(err);
for (var i = 0; i < files.length; i++) {
if (files[i].indexOf('.') == -1)
albums.push({album: files[i]});
}
console.log("read " + files);
eventEmitter.emit('folderRead',files);
});
});
console.log(albums);
eventEmitter.emit('readFolder');
return eventEmitter.on('folderRead',function(files){
console.log("end " + files)
return files;
});
}
},
PagitionHide:'hide'
});
i had a problem to set albums without the eventEmitters, its seems like, doing:
gallery:function(){
var albums = [],
albumDir = './public/img/albums';
fs.readdir(albumDir, function (err, files) {
if (err) console.log(err);
for (var i = 0; i < files.length; i++) {
if (files[i].indexOf('.') == -1)
albums.push({album: files[i]});
}
console.log(albums); //logs the folders names.
});
console.log(albums); //logs an empty array
return albums;
}
cause albums to be empty, i guess beacsue of the async of node.js.
but now when the HTML is render its showing and object like so: [object Object]
what am i doing wrong here?
Did you try this ?
{{#each gallery}}
{{album}}
{{/each}}

chrome extension read value problem

I'm building my chrome extension and I've got weird problem. This is script I'm running in background page:
function getOpenedTabs() {
var openedTabs = [];
chrome.windows.getAll({}, function(wins) {
for (var w in wins) {
if (wins[w].id !== undefined) {
chrome.tabs.getAllInWindow(wins[w].id, function(tabs) {
for (var t in tabs) {
if (tabs[t].id !== undefined) {
openedTabs.push(tabs[t]);
}
}
});
}
}
});
return openedTabs;
}
chrome.tabs.onCreated.addListener(function(tab){
var openedTabs = getOpenedTabs();
var length = openedTabs.length;
console.log("Quantity of tabs: " + length );
if (length > 20) {
openedTabs.sort(function(a,b){return a.visitCount - b.visitCount});
var t = openedTabs.shift();
chrome.tabs.remove(t.id);
console.log("The extension closed the " + t.title + " tab");
}
});
In debugging mode openedTabs.length returns correct value. But when I removed all breakpoints then openedTabs.length returns zero all time.
What kind of problem it might be?
Thanks.
Chrome API calls are asynchronous (think ajax calls), so they are not executed in order. You can't return from such methods, you need to use callbacks.
function getOpenedTabs(callback) {
chrome.windows.getAll({populate: true}, function(wins) {
var openedTabs = [];
for (var w=0; w<wins.length;w++) {
for (var t=0;t<wins[w].tabs.length;t++) {
if (wins[w].tabs[t].id !== undefined) { //I don't think this check is needed
openedTabs.push(wins[w].tabs[t]);
}
}
}
if(callback) {
callback(openedTabs);
}
});
}
chrome.tabs.onCreated.addListener(function(tab){
getOpenedTabs(function(openedTabs) {
var length = openedTabs.length;
console.log("Quantity of tabs: " + length );
if (length > 20) {
openedTabs.sort(function(a,b){return a.visitCount - b.visitCount});
var t = openedTabs.shift();
chrome.tabs.remove(t.id);
console.log("The extension closed the " + t.title + " tab");
}
});
});
You don't need to use getAllInWindow(), you can get all tabs with getAll(). Also using in to iterate over an array isn't a good practice.

Resources