PREFACE
So it seems I've coded myself into a corner again. I've been teaching myself how to code for about 6 months now and have a really bad habit of starting over and changing projects so please, if my code reveals other bad practices let me know, but help me figure out how to effectively use promises/callbacks in this project first before my brain explodes. I currently want to delete it all and start over but I'm trying to break that habit. I did not anticipate the brain melting difficulty spike of asynchronicity (is that the word? spellcheck hates it)
The Project
WorldBuilder - simply a CLI that will talk to a MySQL database on my machine and mimic basic text game features to allow building out an environment quickly in my spare time. It should allow me to MOVE [DIRECTION] LOOK at and CREATE [ rooms / objects ].
Currently it works like this:
I use Inquirer to handle the prompt...
request_command: function(){
var command = {type:"input",name:"command",message:"CMD:>"}
inquirer.prompt(command).then(answers=>{
parser.parse(answers.command);
}).catch((error)=>{
console.log(error);
});
}`
The prompt passed whatever the user types to the parser
parse: function(text) {
player_verbs = [
'look',
'walk',
'build',
'test'
]
words = text.split(' ');
found_verb = this.find(player_verbs, words)[0];
if (found_verb == undefined) {
console.log('no verb found');
} else {
this.verbs.execute(found_verb, words)
}
}
The parser breaks the string into words and checks those words against possible verbs. Once it finds the verb in the command it accesses the verbs object...
(ignore scope mouthwash, that is for another post)
verbs: {
look: function(cmds) {
// ToDo: Check for objects in the room that match a word in cmds
player.look();
},
walk: function(cmds) {
possible_directions = ['north','south','east','west'];
direction = module.exports.find(possible_directions, cmds);
player.walk(direction[0]);
},
build: function(cmds) {
// NOTE scope_mouthwash exists because for some
// reason I cannot access the global constant
// from within this particular nested function
// scope_mouthwash == menus
const scope_mouthwash = require('./menus.js');
scope_mouthwash.room_creation_menu();
},
execute: function(verb, args) {
try{
this[verb](args);
}catch(e){
throw new Error(e);
}
}
}
Those verbs then access other objects and do various things but essentially it breaks down to querying the database an unknown number of times to either retrieve info about an object and make a calculation or store info about an object. Currently my database query function returns a promise.
query: function (sql) {
return new Promise(function(resolve, reject){
db.query(sql, (err, rows)=>{
if (err) {
reject(err);
}
else {
resolve(rows);
}
});
});
},
The Problem
Unfortunately I jumped the gun and started using promises before I fully understood when to use them and I believe I should have used callbacks in some of these situations but I just don't quite get it yet. I solved 'callback hell' before I had to experience 'callback hell' and now am trying to avoid 'promise hell'. My prompt used to call itself in a loop after it triggered the required verbs but this whole approach broke down when I realized I'd get prompt messages in the middle of other prompt cycles for room building and such.
Should my queries be returning promises or should I rewrite them to use callback functions handled by whichever verb calls the query? How should I determine when to prompt the user again in the situation of having an unknown number of asynchronous processes?
So, put in a different way, my question is..
Given the parameters of my program how should I be visualizing and managing the asynchronous flow of commands, each of which may chain to an unknown number of database queries?
Possible Solution directions that have occurred to me..
Create an object that keeps track of when there are pending promises and
simply prompts the user when all promises are resolved or failed.
Rewrite my program to use callback functions where possible and force a known number of promises. If I can get all verbs to work off callback functions I might be able to say something like Prompt.then(resolve verb).then(Prompt)...
Thank you for bearing with me, I know that was a long post. I know enough to get myself in trouble and Im pretty lost in the woods right now.
Related
I have a function that posts various updates to the server using ajax. I need to let the user know once all of the updates have been sent to the server.
I have an array of promises then use promise.allSettled then but it seems to continue even though many of the promises are still pending.
$('#updateModal').modal('show');
console.log('Sending Updates To Server');
let DBOpenRequest = window.indexedDB.open('pending-updates');
var promises=[];
var recordsToDelete=[];
DBOpenRequest.onsuccess = function(event)
{
var db = event.target.result;
// db transaction
var itemUpdatePromises = [];
itemUpdatesTransaction = db.transaction('item-updates', 'readwrite'),
// get object store
itemUpdates = itemUpdatesTransaction.objectStore('item-updates'),
// get all records in object store
itemUpdatesRecords = itemUpdates.getAll();
itemUpdatesRecords.onsuccess = () =>
{
if (typeof itemUpdatesRecords.result!=='undefined')
{
if (itemUpdatesRecords.result.length>0)
{
recordsToDelete['itemUpdates']=itemUpdatesRecords.result;
console.log('Sending '+itemUpdatesRecords.result.length+' Item Updates To Server');
addElement('logWindow', '<p>Sending '+itemUpdatesRecords.result.length+' Item Updates To Server <i id="itemUpdateIcon" class="fa-duotone fa-paper-plane"></i></p>')
$.each(itemUpdatesRecords.result, function( index, value )
{
var promise = postItemUpdates(value);
promises.push(promise);
});
}
}
};
itemUpdatesRecords.onerror = () =>
{
//db.close;
console.log('Item Updates Object Store Not Found', itemUpdatesRecords.error);
};
The code above is building the promises array. But looking at the console output in the screenshot im getting the "all updates sent" log message before even "sending 2 item updates to server".
await delay(500); // this allowed the time for the promises array to populate before continuing - not an ideal solution but solved my issue.
console.log("before", promises.join(","))
Promise.allSettled(promises).then(function (values)
{
console.log("after", values.join(","));
const rejected = values.filter(result => result.status === 'rejected').map(result => result.reason);
if ((Array.isArray(rejected) && rejected.length > 0) || typeof rejected === 'undefined')
{
errorMsg('There Was A Problem Sending Updates To Server');
}
console.log('all updates sent')
The screenshot above is the console.log of the promises i am waiting to be resolved. The console.log(values) in the then function are empty and the success message fires straight away even though the promises are still pending. Does anyone know why the then function is firing before the promises are settled?
The output you're seeing is the first console.log statement, before the .allSettled.
Remove that first log statement, and then see what you get.
Some advice about debug logging
You are having trouble figuring out what your program is doing, and that trouble is made worse because you're being lazy about debug logging.
The fact is that when you're suddenly running into trouble, that's the time to become more meticulous rather than less: if you're stuck, it's probably because you misunderstood something, and the only way out of that situation is to become very careful and alert and precise while probing the situation. Put another way: if you overlooked something in haste while writing the original code, you are probably going to overlook the solution if you conduct the search with the same haste.
This is what's wrong:
console.log(promises) on line 1
console.log(values) on line 3
Those log statements do not contain any information other than the raw value they're dumping, which means that if they contain information you are not expecting or do not recognize, you will be unable to make sense of what you see in the logs at runtime.
Remember that console.log does not show you the name of the variable you dumped -- it only shows you the value. So, if somehow both promises and values have the same value assigned to them, then the log statements will look identical.
These log statements are not clear enough to guarantee that you interpret them correctly at runtime, and that is completely your fault.
I call this "lazy" because the solution to this problem is not hard to conceive of. You should just do this:
console.log('promises', promises) on line 1
console.log('values', values) on line 3
The only reason most devs don't do that every time is because it requires more effort, and we all avoid extra effort.
So let's talk about effort.
Which of these two options takes more effort:
Option A: writing minimal debug statements that are not helpful enough to lead you to the answer, so you retry the experiment with more unhelpful log statements a few times hoping it will eventually become clear, and when it doesn't, going to StackOverflow to post a question with sample code and then waiting for other people to diagnose the problem
Option B: writing slightly longer debug statements that print both the value of the variable and its name, so that your log statements are so unambiguous that they probably reveal the core problem on the first test run
This is not a trick question. Option A takes more effort. It always takes more effort. The reason most developers choose Option A is that the first step of Option A is slightly less effort that the first step of Option B; nevermind that Option B has only one step. (Gradient descent affects biological intelligence, too!)
The reason I mentioned earlier that this problem is "your fault" is not to make you feel bad, but because it's important to recognize that this means things will only get better if you change your methods. The bad experience you're having is not the result of faulty technology or shoddy tools. You have amazing tech and tools at your disposal. You're getting bad results because you're not using them well. Start using them well, and you will start getting better results.
I did my logging just like your code sample for nearly 20 years, and although I was never so hopelessly stuck that I posted about it online, the truth is that, just like you, I often found the log results perplexing.
Then one day I started doing all my debug logging like this:
console.log(`someVar`, JSON.stringify(someVar))
Note:
prints the variable name first
stringifies the value
It's hard to overstate the impact this one tiny change had on my day-to-day experience. From that point forward, debug logging has become an extremely reliable tool. I don't always have to resort to debug logging to figure out a problem, but now I can almost always count on it to find the problem in a single test run.
Of course, I am also a very lazy person, and so eventually I got tired of writing all that out. I still did it every time because it was so important, but it bothered me.
It turns out that many IDEs have sophisticated code-completion features. So, unless you're doing all your coding with Notepad, you can probably do what I did and set up a snippet. Here's mine, in VS Code. It triggers when I type dump and then hit Tab:
This question already has answers here:
Returning a value from callback function in Node.js [duplicate]
(4 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I don't understand callbacks in nodejs.
I need to get a podcast number from the database and use it in my code
I get right now from console.log
[Function: index]
Is there any solution in node.js to get variable from a database and reuse it later in the code?
var index = function( callback ) {
var podcast = 0;
User.findOne({ sessionID: this.event.session.sessionId }, function(err, user) {
if (err ||!user){
}
else {
console.log(user);
podcast = user.podcast;
}
});
callback( podcast );
};
index();
var callback = function(data) {
return data;
}
var iUseMyAnywhere = callback;
It look like it is impossible what I want to do in Node.js
It's completely possible. Only, you need to use asynchronous API's, which makes it ankward.
Is there any solution in node.js to get variable from a database and reuse it later in the code?
Not exactly. When you connect to a database -or, by the way, do anything asynchronously, like fetching something over http or reading from disc- you can't assign that thing right over:
var myUserFromDb = User.find('john doe', function(err, res){...}); //this will fail
Because that function you're passing as the second parameter will execute sometime in the future. User.find() itself doesn't return the user.
So, sadly, you can't just get the user in the user var and pass it to another module -let's say a podcast module-.
However, let's say you have a 'user.js' module, with exposes a withUser method than aks the database for a user and then calls a provided function with the user, when the db call is resolved.
And let's say you have a 'podcast.js' file/module with a getPodcast method that needs a user.
getPodcast can't just ask 'user.js' a user. However, it can ask for a function that will run with the user passed as parameter:
User.js
function withUser(callback){
User.find({_id: 1}, (err, user)=> {
callback(user);
})
}
podcast.js
function getPodcast(){
withUser( function(user){
//now we really have the user inside podcast.js, and we can work with it.
//Sadly, that will surely involve more asynchronous api's, which is painful.
})
}
Now getPodcast will have access to the user inside its parameter callback.
Is there any easier method rather than callback?
Yes, you should read about promises. When using promises, things are -a little less- painful. A promise api would work as:
User.js
function getUser(id){
//let's say we return a promise that wraps the `User.find` database request
}
podcast.js
getUser(userId).then(user => getPodcast(user)).then(podcastResult => ...)
This don't see really better. However, when you are working with promise api's, you can then start using async/await.
podcast.js
async function getPodcast(userId){
const user = await User.getUser(uesrId);
const otherAsyncThing = await ...someAsyncApiCall;
doAnythingWithUser(user); //this line won't execute until user is resolved, even if there aren't callbacks involved :-D
}
A final, unasked word of advice: when working with node.js, be sure you understand how callback api's and async things really work before writing a ton of code. Otherwise, you'll get really coupled and brittled code, where objects get passed through mountains of callbacks and code is unreadable and undebuggable :-D
PS. Edited to follow question.
Think like this, you get to a restaurant, sit down and ask a waitress for a coffee. But in the meanwhile you are not frozen, you are moving, doing things, talking, so once your coffee is ready, the waitress will bring it to you and you will stop other things that you are doing and drink your coffee.
So, it would become something like this:
User.findOne({ sessionID: this.event.session.sessionId }).exec().then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
I recently did a lot of coding in AngularJS. After some time it started to feel comfortable with it and also got really productive. But unfortunately there is this one thing I don't understand:
Within my project I need to get data through $http.get and a RESTful API server. This is where I started to stumble first. After implementing promise ($q.defer etc and .then) at functions which are processing data that's necessary to continue, I thought I conquered the problem.
But in this code:
$scope.getObservationsByLocations = function() {
var promise = $q.defer();
var locationCount = 0;
angular.forEach($scope.analysisData, function(loc) { // for each location
$http.get($scope.api + 'Device?_format=json', { // get all devices
params: {
location: loc.location.id
}
}).then(function (resultDevices) {
var data = angular.fromJson(resultDevices);
promise.resolve(data);
// for each device in this location
angular.forEach(angular.fromJson(resultDevices).data.entry.map(function (dev) {
http.get($scope.api + 'Observation?_format=json', { // get all observations
params: {
device: dev.resource.id
}
}).then(function (resultObservations) {
var observations = angular.fromJson(resultObservations);
// for each obervation of that device in this location
angular.forEach(observations.data.entry.map(function(obs) {
$scope.analysisData[locationCount].observations.push({observation: obs.resource});
}));
})
}))
});
locationCount++
});
return promise.promise
};
I can't understand in which order the commands are executed. Since I use the Webstorm IDE and it's debugging feature, it would be more accurate to say I don't know why the commands are executed in an order I don't understand.
Thinking simple, everything included in the forEach have to be executed before the return is reached, because $http.get's are connected through .then's. But following the debugging information, the function iterates over locationCount++ and even returns the promise before it goes deeper (meaning after the first .then() ).
What's that all about? Did I misunderstood this part of the AngularJS concept?
Or is this just really bad practice and I should reach out for a different solution?
If the context is important/interesting: Objects are based on i.e. https://www.hl7.org/fhir/2015May/location.html#5.15.3
With JavaScript you can create only single thread applications, although e.g. here they say it is not guaranteed to be like that.
But we are talking about the real world and real browsers, so your code sample is running as a single thread (by the way the same thread is also used for rendering your CSS and HTML, at least in Firefox).
When it comes to an asynchronous call
$http.get($scope.api + 'Device?_format=json', {
it says "hey, I can do that later". And it waits with that because it must go on with the current thread.
Then, once the current task is done with return it finally can start getting the remote data.
Proof? Check this fiddle:
console.log(1);
for (var i=0;i<1000000;i++) setTimeout(function(){
console.log(2);
},0);
console.log(3);
You see the spike with the for loop? This is the moment when it registers the setTimeout asynchronous calls. Still 3 is printed before 2 because the task is not done until the 3 is printed.
The $http.get is asynchronous, so depending on (among other things) how large the fetched data is, the time it takes to 'complete' the get is variable. Hence why there is no saying in what order they will be completed
I'm not so experienced/elegant programmer. I hope my question is understandable.
I have used java/c++ for most part of my life so my mind is object oriented. Then I learned python and I enjoyed very much functional mind. Now I'm approaching js. It is challenging because it is function based (and I liked it a lot), functional (with underscore I have a lot of pythonic iterutils methods) and asynchronous (it hurts).
A lot of time I have some back-end sync procedural flow like:
if exist(user):
foo(user)
# ok
else:
add(user)
# user added
Now I have to handle this with the so called callback hell:
exists(user, function(userExist) {
if( userExist ) {
foo( user, function(fooResult) {
/* ok */
});
} else {
add( user, function(addResult) {
/* user added */
});
}
});
Sometimes the controls are more nested: check for token, if token is valid check for user exists, if user exists check for valid parameters, then check for no error in foo on user, etc…
Those controls are simply synchronous and imperative-like. Nothing else to say. While with sync language like python I can handle this is (not elegant but at least) readable code with the use of small function that returns values , with javascript I don't know how to refactor things in readable small function. All functions I wrote does not have any return statements but just callback(something_weird_defined_in_caller_function) and I lost myself.
I don't think that promises are good on my case because are more for piping events, IIUC. So I'm looking for some pattern using async library to handle this case:
- continue to execute functions in series only if the previous one succeeded.
Please any help will be appreciated.
I don't think that promises are good on my case because are more for piping events, IIUC
No. Promises don't represent a stream of events to pipe, but a single result that will arrive later (asynchronously). This is exactly what they were made for, and they will give you imperative-like looking code (instead of a nesting callback hell).
Admittedly, you still need nesting for control structures. The code that you presented won't look much different except for the callbacks going into .then() calls, and promises being returned.
However, you can also use exceptions for control flow. Instead of using exists that returns a Promise<boolean>, you could use a find function that returns a Promise<User> which would get rejected when the user doesn't exist. It could be used like
find(user).then(function(/*user*/) {
return foo(user) /* .then(function(fooResult) {
…
}); */
}).catch(UserNotFoundError, function(e) {
return add(user) /* .then(function(addResult) {
…
}); */
});
Which of those to choose is debatable, rejections should be exceptional.
Advice from a Parse developer forum said to "limit saveAll to 75 objects unless one wants saveAll to make its own batches" which by default are 20 objects. And to put this in a promise chain.
I need to do a saveAll promise chain where I don't know how many promises I need.
How would this be done?
I have an Array of Arrays. The sub arrays are all length 75. I need all the indexes of the master array to be saveAll in a Promise each.
var savePromises = []; // this will collect save promises
while((partition=partitionedArray.pop()) != null){
savePromises.push(Parse.Object.saveAll(partition, {
success: function(objs) {
// objects have been saved...
},
error: function(error) {
// an error occurred...
status.error("something failed");
}
}));
}
return Parse.Promise.when(savePromises);
}).then(function() {
// Set the job's success status
status.success("successful everything");
A nice way to do this is to build the chain of promises recursively. If you've already batched the objects that need saving into batches, then some of the work is done already.
// assume batches is [ [ unsaved_object0 ... unsaved_object74 ], [ unsaved_object75 ... unsaved_object149 ], ... ]
function saveBatches(batches) {
if (batches.length === 0) { return Parse.Promise.as(); }
var nextBatch = batches[0];
return Parse.Object.saveAll(nextBatch).then(function() {
var remainingBatches = batches.slice(1, batches.length);
return saveBatches(remainingBatches);
});
}
EDIT - To call this, just call it and handle the promise it returns...
function doAllThoseSaves() {
var batches = // your code to build unsaved objects
// don't save them yet, just create (or update) e.g....
var MyClass = Parse.Object.extend("MyClass")
var instance = new MyClass();
// set, etc
batches = [ [ instance ] ]; // see? not saved
saveBatches(batches).then(function() {
// the saves are done
}, function(error) {
// handle the error
});
}
EDIT 2 - At some point, the transactions you want to won't fit under the burst limit of the free tier, and spread out (somehow) won't fit within the timeout limit.
I've struggled with a similar problem. In my case, it's a rare, admin-facing migration. Rare enough and invisible to the end user, to have made me lazy about a solid solution. This is kind of a different question, now, but a few ideas for a solid solution could be:
see underscore.js _.throttle(), running from the client, to spread the transactions out over time
run your own node server that throttles calls into parse similarly (or the equal) to _.throttle().
a parse scheduled job that runs frequently, taking a small bite at a time (my case involves an import file, so I can save it quickly initially, open it in the job, count the number of objects that I've created so far, scan accordingly into the file, and do another batch)
my current (extra dumb, but functional) solution: admin user manually requests N small batches, taking care to space those requests ("one mississippi, two mississippi, ...") between button presses
heaven forbid - hire another back-end, remembering that we usually get what we pay for, and parse -- even at the free-tier -- is pretty nice.