Promise allSettled not waiting for promises to resolve - javascript

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:

Related

Text Game: Asynchronous Node - Prompts and MySQL

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.

Is try-catch meant to prevent or handle errors? (in javascript)

Recently I had a discussion with my coworker, about using try and catch to notify errors or avoid them.
This is my coworker's approach:
import Config from 'config';
export const getUserFromLocalStorage = () => {
const key = Object.keys(localStorage).find(value => value === `${Config.applicationId}/currentUser`);
try {
return key ? JSON.parse(localStorage[key]) : {};
} catch (e) {
return {};
}
};
Wich means, he doesn't care about the given error and he is just carrying of returning an object in order to continue the process
and mine is:
import Config from 'config';
export const getUserFromLocalStorage = () => {
const key = Object.keys(localStorage).find(value => value === `${Config.applicationId}/currentUser`);
try {
return key ? JSON.parse(localStorage[key]) : {};
} catch (e) {
console.log('the given error', e); // Just simple notifier for this example
}
};
but my approach, still has a problem which is that it will return undefined (which can crash internaly my app), that can easily fix it using finally and returning a default value but it doesn't sound a good practice to me.
THE QUESTION
So what will the balance using try catch and finally if needed, to make my application stable.
Is there something wrong with our approach?
In particular, we can't trust the data that is comming from the localStorage, so what will be the best approach for this implementation?
Since finally is executed in either case, whether something was thrown or not, it is not the place to return a default value. It is also questionable whether you need to log the error in great detail or not. It all depends on whether something is an expected error or a truly exceptional circumstance and who can do something about it.
Is it rather likely or possible that the value stored is invalid JSON? And you have a "backup plan" for what to do in that case? And there's nothing the user and/or developer can do about it? Then don't bother anyone with it. Maybe you want to console.log a message that might aid in debugging, but other than that just carry on with the program flow. There's most certainly no need to bug the user with an alert if a) the user didn't initiate the action and b) there's nothing for them to do about it either.
Considerations to take:
Whether to catch an error in the first place:
is it an expected error which may naturally occur during the program flow?
is it an error you can do something about?
do you have any plan what to do if you caught the error?
Whether to log an error:
does this log do anyone any good?
will anybody ever see that log entry?
does it give anyone any useful information that can lead to fixing the issue?
Whether to bug the user about something:
did the user initiate the action?
does the user expect some form of response, positive or negative?
can the user do anything to fix the problem?
Whether to return an empty object or nothing/null/undefined depends on what the function's responsibility is. Is the function defined to always return an object? Then it should return {} from catch. Or is "nothing" a valid response when the expected object doesn't exist? Then perhaps return false.
Overall, your coworker's approach seem very reasonable to me.
In this specific case, where you are working with localStorage (which almost always inevitably means working with JSON.parse()), it's always best practice to wrap your processing in try-catch. This is because both localStorage and JSON.parse have exceptions as a normal part of their error handling and it is usually possible to fallback to a default or initial value gracefully.
A pattern I use is something like as follows:
const DEFAULT_VALUE = {};
try {
const result = JSON.parse(result);
return result || DEFAULT_VALUE;
} catch (e) {
console.warn('Error parsing result', e);
}
return DEFAULT_VALUE;
This way, you have consistent error handling and default value fallback.
In general, you shouldn't need use try-catch unless you can and will safely handle the error and generate a useful fallback. Most try-catch blocks tend to sit at the bottom of a call-stack for this reason, so that they catch unplanned errors, handle them gracefully for the user, but log them noisily with a call-stack to the console for the developer to investigate/handle correctly/workaround.
I think the most important thing is that user satisfaction. At the end of the day, the program is used by the normal user. The user needs to continue his work using the program without any interruptions.
So i think that the best practices is to use try to run code and catch if there any errors and notify developer and/or user that there is an exception, and use finally to overcome the exception by returning a valid object.
In this way user is also able to continue the work and the developer also can check the error in log file for future debugging.
This is my personal idea.

equivalent GET request in node environment and browser not showing the same results

I am starting to delve a bit more deeper into node.js and am playing around in the termianl. I am making a GET request, using the got node package https://www.npmjs.com/package/got
Below is both my code using jquery in the browser and got in the terminal.
jQuery Code
$.get('https://raw.githubusercontent.com/notwaldorf/emoji-translate/master/bower_components/emojilib/emojis.json', function (data) {
var showEmoji = JSON.parse(data)
console.log(showEmoji.grinning.char);
})
This displays 😀 in the broswer's console as I expected -- all is good.
Then when I write the equivalent code in the node environment, nothing is displayed.
Node.js code
var got = require("got");
var url = 'https://raw.githubusercontent.com/notwaldorf/emoji-translate/master/bower_components/emojilib/emojis.json'
got(url).then(function(data){
var showEmoji = JSON.parse(data)
console.log(showEmoji.grinning.char);
});
As far as I can tell node does recognise these emoji symbols -- see screenshot
Can anyone shed any light on why the results are not the same? Am I missing something simple and obvious?
It doesn't appear that the issue is to do with the emoji, as if I try change the code in the callback funxtion to the below, still nothing is displayed in node, but an array is displayed in the browser console.
console.log(showEmoji.grinning.keywords);
The problem is that data is not a string, it's a rich object describing the HTTP request. This code works:
var got = require("got");
var url = 'https://raw.githubusercontent.com/notwaldorf/emoji-translate/master/bower_components/emojilib/emojis.json'
got(url).then(function(data){
var showEmoji = JSON.parse(data.body);
console.log(showEmoji.grinning.char);
});
...but you really should be adding .catch( ... ) calls to your promises, to alert you when this kind of thing happens. (There's also a common callback .done() but the promises from got don't seem to support it.)
The reason it didn't work is that the JSON.parse function is failing — but the reason Node didn't tell you about that is that exceptions in .then() callbacks aren't shown to the user. They're stored and passed into .catch() callbacks or thrown when you call .done(). Generally, you should never use .then() without .catch() existing somewhere or else this kind of thing will keep happening.
It would be lovely if all this explicit handling wasn't needed but that was never possible with promise libraries. Perhaps native promises will allow it in the future.

AngularJS $http.get async execution order

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

JavaScript Double Null Check and Locking

In a language with threads and locks it is easy to implement a lazy load by checking the value of a variable, if it's null then lock the next section of code, check the value again and then load the resource and assign. This prevents it from being loaded multiple times and causes threads after the first to wait for the first thread to complete the action that's needed.
Psuedo code:
if(myvar == null) {
lock(obj) {
if(myvar == null) {
myvar = getData();
}
}
}
return myvar;
JavaScript runs in a single thread, however, it still has this type of issue because of asynchronous execution while one call is waiting on a blocking resource. In this Node.js example:
var allRecords;
module.exports = getAllRecords(callback) {
if(allRecords) {
return callback(null,allRecords);
}
db.getRecords({}, function(err, records) {
if (err) {
return callback(err);
}
// Use existing object if it has been
// set by another async request to this
// function
allRecords = allRecords || partners;
return callback(null, allRecords);
});
}
I'm lazy loading all the records from a small DB table the first time this function is called and then returning the in-memory records on subsequent calls.
Problem: If multiple async requests are made to this function at the same time then the table is going to be loaded unnecessarily from the DB multiple times.
In order to solve this I could simulate a locking mechanism by creating a var lock; variable and setting it to true while the table is loading. I would then put the other async calls into a setTimeout() loop and check back on this variable every (say) 1 second until the data was available and then allow them to return.
The problems with that solution are:
It's fragile, what if the first async call throws and doesn't unset the lock.
How many times do we loop back into the timer before giving up?
How long should the timer be set for? In some environments 1 second might be way too long and inefficient.
Is there a best practise for solving this in JavaScript?
On the first call to the service, initialize an array. Start the fetch operation. Create a Promise, store it in the array.
On subsequent calls, if the data is there, return an already-fulfilled Promise. If not, add another Promise to the array and return that.
When the data arrives, resolve all the waiting Promise objects in the list. (You can throw away the list once the data's there.)
I really like the promise solution in the other answer -- very clever, very interesting. Promises aren't the dominent methodology, so you may need to educate the team. I'm going to go in another direction though.
What you're after is a memoize function -- an in-memory key/value cache of expensive results. JavaScript the Good Parts has a memoize sample towards the end. Lodash has a memoize function. These assume synchronous processing so don't account for your scenario -- which is to say they'd hit the database lots of times until one of the "threads" replied.
The async library also has a memoize function that does exactly what you want. In it's innards, it keeps a queue array of callbacks, and once it gets the answer, it both caches it and calls all the callbacks.
If you're into inventing, by all means, use promises. If you'd just like a plug-n-play answer, use async#memoize.

Categories