This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed last month.
I'm new to js and I hardly use it. I'm more into java, but I have this little piece of code where I want it to return the content of the variable, which in turn brings the name of the file inside a folder, I need it to send it by mail and then move it from that folder, when I run the program it does not return the value of the field but an "undefined" when I run it sending it as a parameter of the function if it has a value, but I want that when listing this folder I return the value to work in a main function where it handles the other three functions (call file, send by mail, move it to another folder) I already have these three individually but I have the same problem that they return undefined
function listar(){
var name;
fs.readdir("//URL/ConfirmadosPDF/", function (err, archivos) {
if (err) {
onError(err);
return;
}
console.log(archivos[0]);
if(archivos.length === 0){
parar();
}else{
//enviarCorreo(archivos[0]);
//moveFile(archivos[0]);
name = archivos[0]
}
/*
for (let index = 0; index < 10; index++) {
const element = archivos[index];
console.log(element)
}*/
});
return name
};
What I need is to do a succession of steps if the folder has files to send them by mail, but if not, if you send them, tell me if you sent it, and if I sent it, move it to a new folder, if it fails in any of these three steps would like to be able to handle it in a master function
This is an asynchronous function, the return runs first, than after a while a callback function is executed.
Try easier fs.readdirSync synchronous version.
First of all, does not existe de variable archivo.
I think you want to return the name.
And the path is wrong, You should use path.join
The func readdir is async
Related
This question already has answers here:
How do I return the accumulated results of multiple (parallel) asynchronous function calls in a loop?
(5 answers)
Closed 6 years ago.
I have this function
function add_cnh(arr_clelem){
var id=arr_clelem.getElementsByClassName("present")[0].id;
var date=arr_clelem.getElementsByClassName("present")[0].getAttribute('date');
var tt_entry= arr_clelem.getElementsByClassName("present")[0].getAttribute('tt_entry');
//new Ajax.Updater('register', '/some_url', { method: 'get' });
new Ajax.Request('/attendances/new',
{
parameters:'id='+id+'&date='+date+'&timetable_entry='+tt_entry+'&subject_id='+subject_id,
asynchronous:true,
evalScripts:true,
method:'get'
/*onSuccess: function(transport) {
var response = transport.responseText || "no response text";
alert("Success! \n\n" + response);
}*/
}
)
var ret=modal_data;
// $$('.MB_close').invoke('observe', 'click', _deinit);
return ret;
}
This function takes html-elements-object as an argument and basically render a modal-box and that modal box contain a form -elements which i need to store inside an array. The variable modal_data contains the elements which I require and its a global variable define in another file.
My problem is
This is a very old project using many JavaScript frameworks and libraries which date back to 2006 the library responsible for opening the model box itself is deprecated as can be seen here
And somehow I don't want to get into server side so I am using a for loop something like this
for(var i=0; i<arr_of_elements.length, i++)
{
my_arrvar[i]=add_cnh(arr_of_elements[i]);
}
Now with each itteration since I want the modal box to get closed and store the data within 'my_arrvar' which is somehow not possible as the call is asynchronous in nature and I've used closures and callbacks but no success. I don't want to use any sort of timer. So this is how it goes
Call the function and get data for each call and remove the modal box by id.
Also can this be used somehow and if then how?
You have to pass in ajax request asynchronous:false instead of true. Otherwise its not possible by another way.
Other Way using jQuery
The easiest way is to use the .ajaxStop() event handler:
$(document).ajaxStop(function() {
// place code to be executed on completion of last outstanding ajax call here
});
See jQuery Event handler
I just want to check if the data I'm about to insert allready exists on my Firebase, and if so I just want to break the add function:
FBDB.addCampain=function (campain){
CampiansRef.once('value',function(snapshot){
snapshot.forEach(function(childSnapshot){
if(campain.Name==childSnapshot.val().Name){
console.log("campain allredy exists on the DB");
return false; //I want to break the addCampain function from here!
}
});
});
var newCampainRef = CampiansRef.push();
campain.id = newCampainRef.key();
newCampainRef.set(campain,function(error){
if(error){
console.log("an error occured the campain did not add to the DB, error:" ,+error);
return false;
}
else{
console.log("campain succssesfuly added to the DB");
return true;
}
});
};
What currently happens is that even if the campaign exists on the database it still continues to the actual adding code. There must be a way to "break" the addCampain function within an anonymous function inside it, or even pass the "return false" up to the main scope.
If you add a few console.log statements, you'll be able to see how your code flows:
console.log('1. starting call to Firebase');
CampaignsRef.once('value',function(snapshot){
console.log('3. for value from Firebase');
snapshot.forEach(function(childSnapshot){
console.log('4. inside childSnapshot');
if (campaign.Name==childSnapshot.val().Name){
console.log("campaign already exists on the DB");
return false;
}
console.log('5. done with snapshot.forEach');
});
});
console.log('2. we started the call to Firebase');
The output will look like:
1. starting call to Firebase
2. we started the call to Firebase
3. for value from Firebase
4. inside childSnapshot
4. inside childSnapshot
4. inside childSnapshot
5. done with snapshot.forEach
This is probably not entirely what you expected. 2. is at the end of the code block, but it fires right after 1. which is at the start. This is because on starts an asynchronous load of the data from Firebase. And since this takes time, the browser continues with the code after the block. Once the data is downloaded from Firebase's servers, it will invoke the callback and you can do what you want. But by then, the original context has finished.
There is no way in JavaScript to wait for an asynchronous function to finish. While you might appreciate if such a way existed, your users would be frustrated by the fact that their browser locks up while your call to Firebase is out.
Instead you have two options:
pass a callback into the function
return a promise
I'm going to use option 1 below, because it is what the Firebase JavaScript SDK already does.
FBDB.addCampaign=function (campaign, callback){
CampaignsRef.once('value',function(snapshot){
var isExisting = snapshot.forEach(function(childSnapshot){
if(campaign.Name==childSnapshot.val().Name){
return true; // this cancels the enumeration and returns true to indicate the campaign already exists
}
});
callback(isExisting);
});
};
You'd invoke this like:
FB.addCampaign(campaign, function(isExisting) {
console.log(isExisting ? 'The campaign already existed' : 'The campaign was added');
};
Note that loading all campaigns from the server to check if a specific campaign name already exists is pretty wasteful. If you want campaign names to be unique, you might as well store the campaigns by name.
CampaignsRef.child(campaign.Name).set(campaign);
From the Firebase documentation, snapshot.forEach returns true if your callback function "cancels the enumeration by returning true". So just change your return false to return true!
Set a "global" (to your addCampaign function) flag in the forEach loop just before breaking out of it, then check this flag when you get back into your main function and return if it is set.
I want to execute 2 functions in a specific that I have imported from 2 other .js files that I have made. The function that needs to complete first takes a bit of time and the 2nd one starts before the first is ended and I need files the first one created for it to work. Here's basically what my .js looks like:
var pdfToPng = require("./pdfToPng.js");
var doStuffToPng = require("./doStufftoPng.js");
var pdfFilePath = process.argv[2];
var pngFilePath = pdftoPng.convert(PdfFilePath);//convert takes a path
//and makes a png and returns path
//to the png
doStuffToPng.doStuff(pngFilePath);
//I want "doStuff()" to start AFTER "convert()" is done.
Im pretty sure it has something to do with callbacks, but I'm a javascript noob and need help. I can get it to work with setTimeout(), but that seems like a "duct tape fix" to me. Is there some way more elegant?
Edit: some wonderful people wanted to help and asked to post this, the pdfToPng.js:
var spindrift= require('spindrift');//this is a node module
var fs = require('fs');
//Makes a png from pdf in pngFolder and returns the path to that png
exports.convert = function(path)
{
var pdf = spindrift(path);
var pathToPng = path.substring(0, path.length-4); //takes off the .pdf
pathToPng += "_out.png";
//this is spindrift's stuff, makes a png in dir pngFolder/pathToPng
pdf.pngStream(500).pipe(fs.createWriteStream("pngFolder/" + pathToPng));
return "pngFolder/" + pathToPng;
}
Welcome to the async world of javascript. The function callback though created synchronously is executed asynchronously. So you have to modify the code to get doStuff executed only after you know for sure that convert function has executed. You can find how this can be done # Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
if so, you need to implement your own callback,
- Open pdftoPNG.js
- modify convert function with one more parameter
function convert(PdfFilePath, finishConvert) {
//now just insert this line where you finally instead of return
//remove return yourpngpath; //or something, i assume
//add following in the place of return
finishConvert(yourpngpath);
}
Then Please call like this
var pdfToPng = require("./pdfToPng.js");
var doStuffToPng = require("./doStufftoPng.js");
var pdfFilePath = process.argv[2];
pdftoPng.convert(PdfFilePath,function(path){
if(path!="") {
doStuffToPng.doStuff(path);
}
});
You have to update your convert method to support callbacks/ promises.
Here is an example using Callbacks
exports.convert = function(path, fnCallback)
{
var pdf = spindrift(path);
var pathToPng = path.substring(0, path.length-4); //takes off the .pdf
pathToPng += "_out.png";
//this is spindrift's stuff, makes a png in dir pngFolder/pathToPng
pdf.pngStream(500).pipe(fs.createWriteStream("pngFolder/" + pathToPng));
if (fnCallback && typeof(fnCallback) === "function") {
fnCallback("pngFolder/" + pathToPng);
}
}
You'll see the following
A new parameter being passed in fnCallback
A check to make sure that the parameter is passed in and that it is a function
The fnCallback function gets called with the results passed in as a parameter
Removing of the return statement
Now when the convert method is called, after the long running process completes, the Callback method will get executed.
To call the modified convert method you now have to pass in a callback function
function myCallback(path){
// do something with the path
}
pdftoPng.convert(PdfFilePath,myCallback);
I am using https://github.com/gpittarelli/node-ssq to query of a bunch of TF2 game servers to find out if they are on, and if so, how many players are inside.
Once I find a server that is on and has less than 6 players in it, I want to use that server's Database ID to insert into somewhere else.
Code looks like this:
for (var i = 0;i < servers.length;i++) {
ssq.info(""+servers[i].ip, servers[i].port, function (err, data) {
serverData = deepCopy(data);
serverError = deepCopy(err);
});
if (!serverError) {
if (serverData.numplayers < 6){
//its ok
currentServer = servers[i].id;
i = 99999;
}
}
else {
if (i == servers.length-1){
currentServer = 666;
}
}
}
And then right after I insert into database with https://github.com/felixge/node-mysql .
If I put a console.log(serverData) in there, the info will show up in the console AFTER it inserted into the DB and did a couple other stuff.
So how do I "stop" node, or should I be looking at this / doing this differently?
Update:
A simple solution here is to just move your if statements inside the callback function:
for (var i = 0;i < servers.length;i++) {
ssq.info(""+servers[i].ip, servers[i].port, function (err, data) {
serverData = deepCopy(data);
serverError = deepCopy(err);
// moving inside the function, so we have access to defined serverData and serverError
if (!serverError) {
if (serverData.numplayers < 6){
//its ok
currentServer = servers[i].id;
i = 99999;
/* add an additional function here, if necessary */
}
}
else {
if (i == servers.length-1){
currentServer = 666;
/* add an additional function here, if necessary */
}
}
});
// serverData and serverError are undefined outside of the function
// because node executes these lines without waiting to see if ``ssq.info``
// has finished.
}
Any additional functions within the callback to ssq.info will have access to variables defined within that function. Do be careful with nesting too many anonymous functions.
Original (nodesque) Answer
If ssq.info is an Asynchronous function (which it seem it is), Node is going to immediately execute it and move on, only dealing with the callback function (which you passed as a last parameter) when ssq.info has finished. That is why your console.log statement is going to execute immediately. This is the beauty/terror of node's asynchronous nature : )
You can use setTimeout to make Node wait, but that will hold up every other process on your server.
The better solution, in my opinion, would be to make use of Node's Event Emiters, to:
watch for an event (in this case, when a player leaves a server)
Check if the number of players is less than 6
If so, execute your query function (using a callback)
A good primer on this is: Mixu's Node Book - Control Flow. Also, see this SO post.
You should use a callback,
connection.query('INSERT INTO table', function(err, rows, fields) {
if (err) throw err;
//entry should be inserted here.
});
also the http://www.sequelizejs.com/ library is a bit more matrue, it could be an implementation problem with node-mysql
I'm using Node.js + Express + nodejs-sqlite3 to make a form that when submited will insert a new row on an slite3 database.
On query sucess I want to write certain response.
So the small big problem is just: Modify a string that will be storing the html to be shown, inside the callback function of sqlite3.run()
I read about closures, and passing an object with methods to modify its own attributes. But it seems it's not working. It will pass the object attributes and methods, but no change will remain when the callback function ends. I read that objects will be passed as reference, not copies.
This is the code:
app.post("/insert.html", function(req, res){
function TheBody(){
this.html = "";
this.msg = "";
this.num = "";
}
TheBody.prototype.add = function(string){
this.html = this.html + string;
}
var body = new TheBody();
body.msg = req.body.message;
body.num = req.body.number;
var insertCallback = function(data){
return function(err){
if( err != null){
console.log("Can't insert new msg: " + err.message);
data.add("ERROR-DB");
} else {
console.log("Ok. Inserted: " + data.msg);
console.log(data.html);
data.add("OK - MSG: "+data.msg+" NUM: "+data.num);
console.log(data.html);
}
};
};
var db = new lite.Database('database.db');
var query = "INSERT INTO outbox (message, number) VALUES (?, ?)";
db.run(query, [body.msg, body.num], insertCallback(body) );
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', body.html.length);
res.end(body.html);
}
On server side I'll see
Ok. Inserted: TestString
[Blank space since data.html still has no information]
OK - MSG: TestString NUM: TestNumber [Showing that indeed was modified inside the function]
But on the client side res.end(body.html); will send an empty string.
The object is not being passed as reference.
What's missing in the code, and what simpler alternatives I have to change a string variable inside a callback anonymous function?.
I already know I could use response.write() to write directly on the function if it were more simpler. But I discovered it would only work if I use response.end() inside the callback, otherwise (being outside as it is now) it will meet a race condition where the buffer will be closed before sqlite3.run() be able to use response.write().
-------- Answered --------
As hinted by Justin Bicknell and confirmed by George P. Nodejs-sqlite3 functions are run asynchronously. So I was ending the stream to the client before the callback would be called, thus nothing was being printed.
This was a problem more about "This is SPART- nodejs, so write your stuff according to events'" rather than a logic one. I found this kind of programming kind of convoluted but nobody else than me told me to use nodejs. For those wondering about how one could put some order over the order of queries on the database, nodejs-sqlite3 functions returns a database object that is used to chain the next query.
Since I was printing the information to the client just once in every handled event, the resulting object ended like this:
function TheBody(response){
this.response = response;
}
TheBody.prototype.printAll = function(string){
this.response.setHeader('Content-Type', 'text/html');
this.response.setHeader('Content-Length', string.length);
this.response.end(string);
}
Preferring that to clutter all the code a lot of res.setHeader() lines.
node-sqlite3 methods are, by default, run in parallel (asynchronously). That means that your code is going through this timeline:
Your code calls db.run(...)
Your code calls res.end(...)
db.run completes and calls your callback.
This is the source of a huge number of questions here on SO, so you can almost certainly find a better answer than anything that I could write here in a reasonable amount of time.
I would start here: How does Asynchronous Javascript Execution happen? and when not to use return statement?