问题
In the following code,
Promise.allSettled( [ entry_save(), save_state(), get_HTML() ] ).then( ... );
promises entry_save
and save_state
are both readwrite
database transactions and get_HTML
is readonly
. The two readwrite
transactions could be combined together but that complicates the undo/redo chain that is maintained and it ties the success and rollback of the two together which is undesired.
The entry_save
transaction needs to write before the save_state
transaction. Before moving entry_save
into the Promise.allSettled
that is how it worked because the entry_save
transaction was created prior to those of the others. This MDN article explains how the order in which requests are performed is based upon when the transactions are created independently of the order in which the requests are made.
My question is does the synchronous code of each promise process in the order in which it is placed in the array, such that placing entry_save
first will always result in its transaction being created first and guaranteeing its database requests will be performed first?
Although it works and is quick enough, I'd prefer to not do this:
entry_save().then( () => { Promise.allSettled( [ save_state(), get_HTML() ] ) } ).then( ... );
If it matters, that's not the exactly the way it is written, it's more consistent with:
entry_save().then( intermediate );
where intermediate
invokes the Promise.allSettled
.
Thank you.
To clarify a bit, below is the example given in the above cited MDN document.
var trans1 = db.transaction("foo", "readwrite");
var trans2 = db.transaction("foo", "readwrite");
var objectStore2 = trans2.objectStore("foo")
var objectStore1 = trans1.objectStore("foo")
objectStore2.put("2", "key");
objectStore1.put("1", "key");
After the code is executed the object store should contain the value "2", since trans2 should run after trans1.
If entry_save
creates trans1
and save_state
create trans2
, and all in the synchronous code of the functions, meaning not within an onsuccess
or onerror
handler of a database request or something similar, will the MDN example not hold?
Thus, where @jfriend00 writes,
The functions are called in the order they are placed in the array, but that only determines the order in which the asynchronous are started.
will this order the timing of the write requests by that of the creation of the transactions, since the transactions are created in the synchronous code before the asynchronous can commence?
I'd like to test it but I'm not sure how. If two nearly identical promises are used in a Promise.allSettled
, how can the write request of the first created transaction be delayed such that it takes place after the write request of the second created transaction, to test if it will be written first? A setTimeout should terminate the transaction. Perhaps a long-running synchronous loop placed before the request.
The code at the very end of this question may better illustrate more precisely what I have attempted to ask. It takes the MDN example in the article cited above and spreads it across two promises placed in a Promise.allSettled
, both of which attempt to write to the same object store from within the onsuccess
event of a get
request.
The question was will the same principle in the article of the first transaction created writing before the second transaction created, regardless of the order the requests are made, still hold in this set up. Since the synchronous portions of the promises will process in the order the promises are placed in the array, the transaction in promise p_1
will be created before that of p_2
. However, the put
request in the onsuccess
event of the get
request in p_1
is delayed by the loop building a large string. The question is will p_1
still write before p_2
?
In experimenting with this, I cannot get p_2
to write before p_1
. Thus, it appears that the MDN example applies even in this type of set up. However, I cannot be sure of why because I don't understand how the JS code is really interpreted/processed.
For example, why can the req.onsuccess
function be defined after the request is made? I asked that question sometime ago but still don't know enough to be sure that it doesn't affect the way I attempted to add in a delay here. I know that it won't work the other way around; but my point is I'm not sure how the browser handles that synchronous loop before the put request is made in p_1
to really know for sure that this example demonstrates that the MDN article ALWAYS holds in this set up. However, I can observe that it takes longer for the requests to complete as the number of loop iterations is increased; and, in all cases I have observed, p_1
always writes before p_2
. The only way p_2
writes before p_1
is if p_1
doesn't write at all because of the string taking up to much memory causing the transaction in p_1
to be aborted.
That being said, and returning to the fuller set up of my question concerning three promises in the array of the Promise.allSettled
compared to requiring entry_save
to complete before commencing a Promise.allSettled
on the two remaining promises, in the full code of my project, for reasons I am not sure of, the latter is quicker than the former, that is, waiting for entry_save
to complete is quicker than including it in the Promise.allSettled
.
I was expecting it to be the other way around. The only reason I can think of at this point is that, since entry_save
and save_state
are both writing to the same object store, perhaps whatever the browser does equivalent to locking the object store until the first transaction, which is that in entry_save
, completes and removing the lock takes longer than requiring that entry_save
complete before the Promise.allSettled
commences and not involving a lock. I thought that everything would be ready "in advance" just waiting for the two put
requests to take place in transaction order. They took place in order but more slowly or at least not as quick as using:
entry_save().then( () => { Promise.allSettled( [ save_state(), get_HTML() ] ) } ).then( ... );
instead of:
Promise.allSettled( [ entry_save(), save_state(), get_HTML() ] ).then( ... );
function p_all() { Promise.allSettled( [ p_1(), p_2() ] ); }
function p_1()
{
return new Promise( ( resolve, reject ) =>
{
let T = DB.transaction( [ 'os_1', 'os_2' ], 'readwrite' ),
q = T.objectStore( 'os_1' ),
u = T.objectStore( 'os_2' ),
req, i, t ='', x = '';
req = q.get( 1 );
req.onsuccess = () =>
{
let i, t, r = req.result;
for ( i = 1; i < 10000000; i++ ) t = t + 'This is a string';
r.n = 'p1';
u.put( r );
console.log( r );
};
}); }
function p_2()
{
return new Promise( ( resolve, reject ) =>
{
let T = DB.transaction( [ 'os_1', 'os_2' ], 'readwrite' ),
q = T.objectStore( 'os_1' ),
u = T.objectStore( 'os_2' ),
req;
req = q.get( 1 );
req.onsuccess = () =>
{
let r = req.result;
r.n = 'p2';
u.put( r );
console.log( r );
};
}); }
回答1:
indexedDB will maintain the order of the transactions in order created, except when those transactions do not overlap (e.g. do not involve the same store out of the set of stores each one involves). this is pretty much regardless of what you do at the higher promise layer.
at the same time, maybe it is unwise to rely on that behavior, because it is implicit and a bit confusing. so maybe it is ok to linearize with promises. the only reach catch is when you need maximum performance, which I doubt applies.
- see https://www.w3.org/TR/IndexedDB-2/#transaction-construct
- see are indexeddb/localforage reads resolved from a synchronous buffer?
moreover, promises begin execution at the time they are created. they just do not necessarily end at that time, they end eventually instead of immediately. that means that the calls happen in the order you 'create' the promises that are wrapping the indexedDB calls. which means that it relies on the order in which you create the transactions.
regardless of which promise wins the race. regardless of using promise.all.
also, promise.all will retain order even if promises complete out of order, just fyi, but do not let that throw you off.
回答2:
When you do this:
Promise.allSettled( [ entry_save(), save_state(), get_HTML() ] ).then(...)
It's equivalent to this:
const p1 = entry_save();
const p2 = save_state();
const p3 = get_HTML();
Promise.allSettled([p1, p2, p3]).then(...);
So, the individual function calls you issue such as save_state()
are STARTED in the order specified. But, each of those calls are asynchronous so the internal order of what happens before something else really depends upon what they do inside as they can all be in flight at the same time and parts of their execution can be interleaved in an indeterminate order.
Imagine that entry_save()
actually consists of multiple asynchronous pieces such as first reading some data from disk, then modifying the data, then writing it to the database. It would call the first asynchronous operation to read some data from disk and then immediately return a promise. Then, save_state()
would get to start executing. If save_state()
just immediately issued a write to the database, then it very well may write to the database before entry_save()
writes to the database. In fact, the sequencing of the two database writes is indeterminate and racy.
If you need entry_save()
to complete before save_state()
, then the above is NOT the way to code it at all. Your code is not guaranteeing that all of entry_save()
is done before any of save_state()
runs.
Instead, you SHOULD do what you seem to already know:
entry_save().then( () => { Promise.allSettled( [ save_state(), get_HTML() ] ) } ).then( ... );
Only that guarantees that entry_save()
will complete before save_state()
gets to run. And, this assumes that you're perfectly OK with save_state()
and get_HTML()
running concurrently and in an unpredictable order.
My question is does the synchronous code of each promise process in the order in which it is placed in the array, such that placing entry_save first will always result in its transaction being created first and guaranteeing its database requests will be performed first?
The functions are called in the order they are placed in the array, but that only determines the order in which the asynchronous are started. After that, they are all in-flight at the same time and the internal timing between them depends upon how long their individual asynchronous operations take and what those asynchronous operations do. If order matters, you can't just put them all in an indeterminate race. That's call a "race condition". Instead, you would need to structure your code to guarantee that the desired operation goes first before the ones that need to execute after it.
来源:https://stackoverflow.com/questions/60290456/order-of-promise-array-in-promise-allsettled-and-order-in-which-database-transac