Emscripten Fan
Emscripten Fan

Reputation: 97

indexedDB.open() does not deliver the success event

People usually do this:

var DBOpenRequest = window.indexedDB.open("toDoList");
DBOpenRequest.onsuccess = function(event) {//Good};

If the second line of the code is not executed in a timely manner, the onsuccess will miss the event.

Well, the problem does not happen often because the delay between those two lines is usually very short. But, still, the outcome of those two lines is not deterministic. On my machine, if I simulate 270 ms delay between those two lines, the event will be missed. I think the current signature of the open() is inadequate.

The correct asynchronous design pattern is to set the event handler first, then to start the actual asynchronous operation. The open() function should take a callback as an argument.

Any comments?


Updated questions:

  async function test(delay) 
  {
    var req = indexedDB.open("test");

    //Simulate a delay
    await new Promise(resolve => setTimeout(resolve, delay));

    req.onsuccess = function (evt) {console.log("Good");};
  }
  test(1);

If the delay is 1 ms, the "Good" will be logged. If the delay is 1000 ms like test(1000), the "Good" will not be logged, meaning the event handler is not called.

Upvotes: 1

Views: 291

Answers (1)

Josh
Josh

Reputation: 18690

  1. Review the basics of the EventListener design pattern. indexedDB basically adopts the same pattern used throughout several Javascript components such as DOM elements and XMLHttpRequest, and assumes your familiarity with the pattern.
  2. Review how JavaScript's event loop operates because it is important to understand asynchronous code.

Whether you bind a listener function before or after an event is dispatched is irrelevant within the context of asynchronous code. Basically, the bind that you state occurs later, because it is written in a statement on the following line of code, does not actually occur later. Regardless of where it the line is written (barring some pedantic exceptions), it occurs within the same tick of the event loop as the previous line, the call to open. The event does not fire until, at the earliest, the next event loop epoch, which will always be after the binding occurred in the previous event loop epoch. Always. Guaranteed.

The time delay between the calling of the code that does something that causes an event, and the eventual occurrence and reception of that event, is irrelevant. This delay is related to many other things, like how powerful your machine is, how many resources are available to your pc, how busy your script is trying to do other things, possibly even how much junk you have loaded into the dom, because any of those things could contribute into extending the lifetime of the current epoch of the event loop. The delay is implemented as an indefinite wait period. It is basically coded as "occur in some later event loop epoch". This could be 1 nanosecond later, it could be 10000 seconds later, the amount of the delay is irrelevant. The only relevant concern is that the event triggers in the next event loop epoch, which is some time after everything else occurred in the prior event loop epoch.

The second line will always be executed in a timely manner, because the basic criteria for timely here is simply "in the same epoch of the event loop", and here, timely, again, could mean any amount of time.

The outcome is deterministic. Stop thinking of time ticks as an amount of milliseconds elapsed or something like that. Instead think of time as ticks of an event loop. Each tick can take a variable amount of time. Ticks are ordered. Ticks are serial. Tick 2 will occur after Tick 1. Tick 3 will occur after Tick 2. Etc. This is therefore deterministic with regard to execution order, accounting for variable amounts of time per tick. A tick is just a period of time, and despite the periods perhaps having variable amounts, you can still state claims such as the fact that some period occurs before or after some other period. Also, no two periods overlap, ever (not true concurrency, not actually multi-threaded, not strictly interleaved).

I dunno, imagine a stopwatch, or an old wristwatch, or a clock on the wall. The hand travels around the face of the watch. Let's pretend it takes 1 second for the hand to travel from the starting point, all the way around 360 degrees, and return to the starting point. Each roundtrip, let's call it an epoch, or a tick. We can then count how many roundtrips occur, by counting the number of passes back across the starting point. Basically the number of epochs, basically there is cardinality.

Now imagine two watches. The first watch still takes a full second to travel all the way around. The second watch, however, let's pretend, it is an old slow watch, takes 2 seconds to travel all the way around. Half the frequency. Now, the thing is, even though the timing is different between the two watches, we still can make claims like "watch 1 did 10 roundtrips" and "watch 2 did 5 roundtrips".

Now, take it further. Let's take a watch, and introduce random external factors. There are cosmic rays that introduce sporadic gravitational effects on the speed of the hand as it rotates around the watch face. So, some roundtrips hit little speed bumps, and take longer. So we get a distribution of roundtrip times. We still have an average round trip time, but not a constant round trip time. In roundtrip 1, our watch may make the trip in 1 second. In round 2, it may take 2.5 seconds. In round 3, it may take 1 nanosecond. The time is variable. But, this variability does not prevent us from stating things like "well there were 3 round trips when we observed it", and "well the second round trip occurred after the first one", and "no roundtrip travel ever occurred at the same time as another (round trip 1 and 2 and 3 have no overlap)".

These roundtrips are the epochs of the javascript event loop. Your bindings all take place in roundtrip #1. The event that occurs as result of opening indexedDB never takes place in roundtrip #1, no matter where you write it, in whatever order you write it, etc. The event always occurs in either roundtrip #2, or #3, or basically any epoch after roundtrip #1. Even if the event is magically technically 'ready' in #1, it still will not be exposed until #2 or later.

Because the call to open and the binding of the open listener both occur in the first epoch, it is irrelevant to talk about whether the listener is bound before or after the call to open. Because those all happen in #1, and the open event doesn't happen until at the earliest #2.

Upvotes: 1

Related Questions