Skip to content
This repository has been archived by the owner on Feb 13, 2021. It is now read-only.

Can you schedule more tasks after waiting promises resolve? #11

Open
inexorabletash opened this issue Sep 14, 2016 · 1 comment
Open

Comments

@inexorabletash
Copy link
Owner

Let's dig up the old example of get-fetch-put:

tx.waitUntil((async () => {
  const url = await tx.objectStore('urls').get(k);
  const request = await fetch(url);
  const text = await request.text();
  await tx.objectStore('bodies').put(text, k);
})());

It is conceptually simpler to write as:

  const url = await tx.objectStore('urls').get(k);

  const req_p = fetch(url);
  tx.waitUntil(req_p);
  const request = await req_p;

  const text_p = request.text();
  tx.waitUntil(text_p);
  const text = await text_p;

  await tx.objectStore('bodies').put(text, k);
})());

(See commentary in #3 and #9 about simplifying the x_p/waitUntil(X_p)/await x_p pattern)

... but to do that we need to ensure that after the waiting promise fires we get another "tick" to schedule more work, otherwise the commit will start before control returns to script:

  const text_p = request.text();
  tx.waitUntil(text_p);
  const text = await text_p;

  // text_p will resolve before the next line of code executes, so commit could initiate

  await tx.objectStore('bodies').put(text, k);

One way to address this would be to specify that:

once all of the promises in transaction's extend lifetime promises fulfill:

  1. Let count be the number of promises in extend lifetime promises
  2. Queue a microtask to run the following substeps:
    1. If the transaction has aborted, abort these steps
    2. If the number of promises in extend lifetime promises is greater than count, abort these steps.
    3. If there are any requests in request list with done flag unset, abort these steps.
    4. Set transaction's state to "committing" and the transaction attempts to commit.

That is, don't try to commit immediately but queue a microtask to schedule a commit. That will ensure that microtasks queued on a waiting promise get to execute before we try to commit. But it's still subtle.

Maybe we need to queue a task rather than queue a microtask?

@dfahlander
Copy link

I reacted exactly like @inexorabletash about this. It's not clear in README what implications the "waiting" state has on that transactions, except by the code snippets, where it seems possible to use the transaction as if it was in the active state. I hope that is the case, and that the state remains "waiting" after a transaction has been operated on.

Assuming that waiting state is equivalent to active state when operating on the database, we could see that waitUntil() will be the thing you always begin with after creating a transaction, so that the rest of the code doesn't need to worry about transaction committing but can start rely on promises instead.

If I've understood this correctly, the pattern that everyone will likely use, is to always start a transaction using waitUntil() and then do everything in a promise-returning function:

function doSomethingInTransaction (db) {
    let trans = db.transaction(['items'], 'readwrite');
    return trans.waitUntil((async ()=>{
        // Do your code here
    })());
}

dfahlander added a commit to dexie/Dexie.js that referenced this issue Nov 24, 2016
Inspired by @wwoods solution described  in #374 and the new [indexeddb-promises proposal](inexorabletash/indexeddb-promises#11), I've added the method Dexie.waitFor() that may wait on a non-indexedDB Promise or Promise-returning function to complete and keep transaction alive during the whole lifetime of that task.

Implementation was also inspired by the elegant code in [polyfill.js](https://github.com/inexorabletash/indexeddb-promises/blob/master/polyfill.js) by @inexorabletash.
dfahlander added a commit to dexie/Dexie.js that referenced this issue Nov 25, 2016
Inspired by @wwoods solution described  in #374 and the new [indexeddb-promises proposal](inexorabletash/indexeddb-promises#11), I've added the method Dexie.waitFor() that may wait on a non-indexedDB Promise or Promise-returning function to complete and keep transaction alive during the whole lifetime of that task.

Implementation was also inspired by the elegant code in [polyfill.js](https://github.com/inexorabletash/indexeddb-promises/blob/master/polyfill.js) by @inexorabletash.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants