Promises without guesswork

Async patterns.

DBOPFS storage methods return promises. Learn when to await immediately, when independent operations can overlap, and where reliability matters more than shaving a little waiting time.

The contract

All storage methods are asynchronous.

readyPromise is a promise-valued property. Every public storage method is declared async and returns a promise. A promise represents work that has started or will start; await pauses only the surrounding async function until that promise settles. It does not freeze the browser.

Setup and read-back value
var db=window.dbopfs;
await db.readyPromise;

var saved=await db.set(
    'users',
    'alex.json',
    {name:'Alex',active:true}
);

console.log(saved);

set() resolves to the complete value read back from the file after writing. With append, that is the complete resulting file value—not just the appended fragment.

Dependencies and reliability

Await immediately when the next step depends on success.

Await before showing “Saved,” reading the value back, navigating away, starting a backup, or performing a dependent operation. A try/catch makes failure visible to the user.

Save before continuing
try{
    var saved=await db.set(
        'drafts',
        'current.json',
        {body:'Hello'}
    );

    console.log('Saved',saved);
    // Navigate only after the save succeeds.
}catch(error){
    console.error('Save failed',error);
}

Overlap independent work

Start now, await later.

You do not have to place await on the same line that creates a promise. Keep the promise, do independent synchronous work, then await it before anything depends on the result.

Controlled overlap
var pendingSave=db.set(
    'drafts',
    'current.json',
    {body:'Hello'}
);

console.log('Rendering preview while storage works');

var saved=await pendingSave;
console.log('Durable value',saved);

This can reduce total wall-clock waiting when the work is independent. It does not make the storage operation itself faster.

Independent required results

Use Promise.all() for independent work that must all succeed.

Read two independent records
var [profile,preferences]=await Promise.all([
    db.get('users','alex.json'),
    db.get('settings','alex.json')
]);

console.log({profile,preferences});

Promise.all() reports the first rejection, but it does not cancel operations that already started and it provides no rollback. Use it only when the operations are independent.

Keep every outcome

Use settled results when partial success matters.

setMany(), getMany(), and deleteMany() return results shaped like Promise.allSettled(). Inspect every status; the batch is not atomic.

Match each result to its file
var items={
    'appearance.json':{mode:'dark'},
    'locale.json':{language:'en-US'}
};
var entries=Object.entries(items);
var results=await db.setMany('settings',items);

results.forEach((result,index)=>{
    var [fileName]=entries[index];
    console.log(fileName,result);
});

If partial success is unacceptable, implement application-specific validation and compensation. Inspecting results detects partial success; it does not undo it.

Exceptional pattern

Fire-and-forget is for noncritical work only.

If nothing later depends on completion, you may intentionally continue without retaining the result—but you must handle rejection. Navigation, reload, tab closure, and browser shutdown can interrupt unfinished work.

Noncritical background write
void db.set(
    'telemetry',
    'last-view.json',
    {at:Date.now()}
).catch(error=>{
    console.error('Background save failed',error);
});

Do not use this for important data. Save drafts while the page is active and await critical saves before navigation. Browsers do not promise to finish OPFS work started during unload, beforeunload, or pagehide.

A precise boundary

Ordering is not transaction atomicity.

await sequences the surrounding async function; it does not lock the database or create a transaction. DBOPFS serializes individual same-key set() calls only within one page-local instance.

  • Same-instance set() calls to one table/key run in invocation order.
  • Different keys can overlap.
  • Other tabs, workers, instances, and raw OPFS users do not share the queue.
  • writeFile(), destructive methods, and multi-key batches are not part of a multi-record transaction.
  • A read followed by a write is not an atomic read-modify-write operation.
Lost-update example
async function increment(){
    var value=await db.get(
        'counters',
        'visits.json',
        true
    ) ?? 0;

    await db.set('counters','visits.json',value+1);
}

await Promise.all([increment(),increment()]);
// Both calls can read 0, leaving a final value of 1.

Other contexts

Force fresh reads when another context may write.

Cached and fresh reads
var cached=await db.get('users','alex.json');
var fresh=await db.get('users','alex.json',true);

var keys=['alex.json','sam.json'];
var freshBatch=await Promise.allSettled(
    keys.map(key=>db.get('users',key,true))
);

getMany() has no force option. Use the explicit Promise.allSettled() pattern above for fresh batch reads. Read the full page-local cache guide.

Choose deliberately

Performance and reliability guide

PatternBest useTradeoff
Sequential awaitDependent steps, user-visible save state, destructive work.Wait times accumulate, but order and errors are clear.
Start now, await laterOverlap storage with independent rendering or computation.You must retain and eventually observe the promise.
Promise.all()Independent operations where every result is required.First rejection is reported; no cancellation or rollback.
Batch methods / allSettled()Independent operations where every outcome matters.Can partially succeed; inspect every result.
Fire-and-forgetNoncritical, best-effort telemetry with handled errors.Caller proceeds sooner, but I/O is not faster and reliability is lower.

Parallel syntax adds no throughput to same-key set() calls because DBOPFS queues them. Excessive parallel file operations can also add contention and memory pressure; prefer intentional batches over unbounded loops.

See exact API return types Understand the cache