最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I access indexedDB synchronously? - Stack Overflow

programmeradmin0浏览0评论

The indexedDB has a spec saying that you can access an indexed database synchronously, but it hasn't been implemented yet.

I was just wondering if there is a way to make it synchronous manually,

My JavaScript looks like this,

var trans = databaseAsync.transaction(["mapTile"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("mapTile");
var keyRange = IDBKeyRange.bound(evt.data[0], evt.data[0]);
var cursorRequest = store.openCursor(keyRange);

// can you put some kind of wait in here?

cursorRequest.onsuccess = function(e)
{
    var result = e.target.result;
    if(!!result == false)
    {
    }
}

So can you put something in there to make it wait until the onsuccess method has been called before continuing on?

The reason why I want to do this is the code above is wrapped inside this method,

dojo.extend(esri.layers.ArcGISTiledMapServiceLayer, {
      getTileUrl : function(level, row, col)
      {
          // blah
          return url;
      }

So it is an ESRI tile layer (which will load tiles onto a map on my web page), and that method needs to return the url straight away for a particular tile. It will either be a URL to load the image from if it isn't cached in the database already, or this,

data:image;base64,*BASE64DATA*

Where BASE64DATA is the data from the database if previously cached.

I was previously using the localStorage for this, which works synchronously, but that has a 5MB limit so I thought I would experiment with indexedDB.

The indexedDB has a spec saying that you can access an indexed database synchronously, but it hasn't been implemented yet.

I was just wondering if there is a way to make it synchronous manually,

My JavaScript looks like this,

var trans = databaseAsync.transaction(["mapTile"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("mapTile");
var keyRange = IDBKeyRange.bound(evt.data[0], evt.data[0]);
var cursorRequest = store.openCursor(keyRange);

// can you put some kind of wait in here?

cursorRequest.onsuccess = function(e)
{
    var result = e.target.result;
    if(!!result == false)
    {
    }
}

So can you put something in there to make it wait until the onsuccess method has been called before continuing on?

The reason why I want to do this is the code above is wrapped inside this method,

dojo.extend(esri.layers.ArcGISTiledMapServiceLayer, {
      getTileUrl : function(level, row, col)
      {
          // blah
          return url;
      }

So it is an ESRI tile layer (which will load tiles onto a map on my web page), and that method needs to return the url straight away for a particular tile. It will either be a URL to load the image from if it isn't cached in the database already, or this,

data:image;base64,*BASE64DATA*

Where BASE64DATA is the data from the database if previously cached.

I was previously using the localStorage for this, which works synchronously, but that has a 5MB limit so I thought I would experiment with indexedDB.

Share Improve this question edited May 25, 2014 at 17:10 Josh 18.7k7 gold badges54 silver badges72 bronze badges asked Feb 9, 2012 at 22:39 peterpeter 13.5k24 gold badges85 silver badges143 bronze badges 1
  • 6 year 4 months later...this question still holds value and looks for a decent updated answer!! – NoobEditor Commented Jun 26, 2018 at 4:39
Add a comment  | 

5 Answers 5

Reset to default 3

IndexedDB Sync API is marked as a risky part of the IndexedDB specification and they might be removed due to potential lack of implementations.

I've implemented 'sync' solution using the 'oncomplete' transaction event which guaranties that the current action is finished before starting the next one, and I also use custom semaphore and queue logic which handles the async calls from GUI and ensures that 2 open connections towards IndexedDB database won't happen at the same time.

There is a more general question how to wait for asynchronous functions where you will find an answer: How to block on asynchronous functions in JavaScript

I also found a nice waitFor routine inside Trial Tool.

IE10 supports the sync api, but the indexeddb sync api can only be used inside a web worker. If you are working in the UI thread you need to use the async API.

Easiest way to work with the async API is through promises.

2024 Answer:

Now that we have SharedArrayBuffer, and Atomics.wait(), this should be possible even on the main thread, but I haven't tested it.

It's naturally going to be quite hacky, since browsers try to get you to avoid doing synchronous storage access, since it can be slow (i.e. up to tens of milliseconds - vs memory/RAM access which is several orders of magnitude faster) and will cause the main thread to lag while the data is being fetched.

Here's what I think you'd need to do:

  1. Ensure you're serving your HTML page with COOP/COEP headers which will cross-origin isolate it, which is needed to allow use of SharedArrayBuffer
  2. Create a worker and send it your SharedArrayBuffer, which must be large enough space for "communication" bytes (to signal to the worker that you're making a request for data), and for the largest reasonable number of bytes returned from IndexedDB - e.g. 100MB (since data transfer will happen via this SharedArrayBuffer, and not via postMessage). Instead of pre-allocating 100MB, you could probably dynamically grow it as needed with SharedArrayBuffer.grow().
  3. From the main thread, send a request to the worker via your communication bytes and immediately enter a while loop which checks the communication bytes at each iteration of the loop to see if the worker has finished. The worker notices the communication bytes change via Atomics.wait() and starts getting the requested data asynchronously from IndexedDB.
  4. Once the worker is finished getting the data, it puts the data in the SharedArrayBuffer and appropriately updates the communication bytes to notify the main thread that it's finished.
  5. The main thread's while loop stops since it notices the communication bytes change, and it grabs the data that the worker put in the SharedArrayBuffer, and that's it. You've accessed IndexedDB data from the main thread without await/async/Promise.

Notes:

  • You'd want to make sure your error checking is good/reliable on the worker side, since if it breaks somehow, then your main thread will be permanently stuck in the while loop waiting for the data that will never come. I suppose you could also add a check against Date.now() in the main thread's while loop to ensure that if it takes more than e.g. 5 seconds, it's break out of the loop and throw an error.

IndexedDB is still around, and it's likely one of the best ways to store data for apps that run in users' browsers. One good thing about IndexedDB is that it lets you keep data as native JavaScript objects.

IndexedDB is made on the way that tasks can be done without blocking the main thread. A responsive UI needs asynchronous operations to keep it running, especially for tasks that might take a while to do because they interact with storage.

async function readValue(dbName, storeName, key) {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open(dbName);

        request.onerror = (event) => {
            reject(event.target.error);
        };

        request.onsuccess = (event) => {
            const db = event.target.result;
            const transaction = db.transaction([storeName], 'readonly');
            const store = transaction.objectStore(storeName);
            const getRequest = store.get(key);

            getRequest.onerror = (event) => {
                reject(event.target.error);
            };

            getRequest.onsuccess = (event) => {
                resolve(getRequest.result);
            };
        };
    });
}

// Usage
(async () => {
    try {
        const value = await readValue('db', 'main', 'bla');
        console.log(value);
    } catch (error) {
        console.error(error);
    }
})();
发布评论

评论列表(0)

  1. 暂无评论