0

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')

enter image description here 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?

AKAust
  • 103
  • 9
  • What's your array of promises look like? – mstephen19 Dec 01 '22 at 17:32
  • The promise array is in the screenshot. – AKAust Dec 01 '22 at 17:33
  • 2
    @mstephen19 spoiler alert - an empty array. You can see in the screenshot that was logged initially. Then it got expanded and because of [console.log() shows the changed value of a variable before the value actually changes](https://stackoverflow.com/q/11284663) it showed contents in the array. The promises to the array were added *after* it was logged. – VLAZ Dec 01 '22 at 17:34
  • Cast the arrays to a string to avoid the lazy evaluation in the console, then tell use what the result of this is by [edit]ing your question. `console.log("before", promises.join(","))` and `console.log("after", values.join(","))` – Samathingamajig Dec 01 '22 at 17:38
  • 1
    So, you have async code pushing items in the array. And you're wondering why the array has no items before logging but acquires them after logging. Think about it for a moment. Then also check [this](https://jsbin.com/fujinuk/1/edit?js,output). I've nothing more to add, though. It's getting really tiring trying to convince you that the code you have works like it works. – VLAZ Dec 01 '22 at 19:08
  • Ok so i get how it is happening, i understood that a few comments of yours ago! But how would i make sure I have all of the records from indexed db in the array before i do the all settled? – AKAust Dec 01 '22 at 19:15
  • In the end i just put in a delay of 500ms to grab the data from indexed db before firing the promises all settled. If there is a better way, let me know! – AKAust Dec 01 '22 at 19:50

1 Answers1

1

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:

enter image description here

Tom
  • 8,509
  • 7
  • 49
  • 78
  • When i do console.log(values) in the then function it is empty – AKAust Dec 01 '22 at 17:35
  • 1
    It took me a long time to write out the rest of this advice, and in that time it appears other posters helped you make progress. I'll just note that we're all giving you the same methodological advice: be explicit and careful with your debug logging, or it will be unable to help you unravel _even simple problems_. – Tom Dec 01 '22 at 18:18
  • Thanks for taking the time to write all of this i will take a proper look at it now! – AKAust Dec 01 '22 at 18:20
  • 1
    Stringifying all logs can be just as misleading as logging a live reference. It's not a silver bullet, it should be used only when needed. Serialising objects will convert them to their most basic form that fits JSON: functions/methods are removed, as would keys that have a value `undefined`, and setters (but getters will be evaluated). The prototype will not be traversed, either, just the own properties. `JSON.stringify([Promise.resolve(), Promise.resolve()])` will just do `"[{},{}]"` which in some ways is helpful to see how many items are there. In other ways it's not really helpful. – VLAZ Dec 01 '22 at 18:29
  • When i use your console.log(`promises`, JSON.stringify(promises)) i just get promises [] in the console but there is stuff in there as i output it using a console.log(promises) and the promises show – AKAust Dec 01 '22 at 18:34
  • 1
    @AKAust no, there isn't stuff there. You can *very clearly see* it's an empty array. I've linked you to *why* you get an empty array logged which *when evaluated* will show items in it. You now have posted even more evidence that the array was empty: the line that says "before" does ***not*** have any other content. Which means that in `console.log("before", promises.join(","))` the `.join(",")` produced an empty string. Which only happens when when the array is empty. – VLAZ Dec 01 '22 at 18:37
  • I am going to show the code i am using to build the array, the array definately has stuff when i do the console.log at the end but it may be getting populated after the promises.allSettled() somehow. – AKAust Dec 01 '22 at 18:54
  • 1
    @AKAust if the array definitely has stuff, why doesn't it show anything in the log? Hint: use a debugger to see what happens during the execution and precisely at the times you log. – VLAZ Dec 01 '22 at 19:00
  • @VLAZ I did say it has stuff at the end but it may be getting populated after the all settled, i have added some of the code that creates the array, earlier on in the function too see if that helps – AKAust Dec 01 '22 at 19:02