javascriptlocal-storageweb-componentlocalforage

Is there a way to check if a JavaScript function accepts a callback?


I am trying to add localForage support to the pure-form Web Component by creating a global instance of localForage:

// create a global appStorage object that mimics localStorage API (greater storage)
window.appStorage = localforage.createInstance({
    driver: [
        localforage.INDEXEDDB,
        localforage.WEBSQL,
        localforage.LOCALSTORAGE
    ],
    name: 'product-name',
    version: 1.0,
    storeName: 'app'
});

And assigning it to an instance of pure-form via the storage attribute:

<pure-form src="path-to-schema.json" storage="appStorage"></pure-form>

Internally, pure-form executes window[self.storage] to get a handle to the storage object and uses .getItem, .setItem to set and retrieve values synchronously.

Trouble is localForage is asynchronous, meaning .getItem, .setItem expect to return values via a callback. Therefore my current logic won't work:

// get value from web storage
var storedContent = window[self.storage].getItem('item-key');

I realise I could wrap the call in a Promise, but as it stands pure-form does not require promises and I'd hate to add that dependency just for this.

What I would like to do is check if .getItem or .setItem require a callback, and if so, modify the code accordingly...


Solution

  • As @Dave-Newton pointed out in the comments:

    there's no way to tell without looking at the source or docs. That said, almost any async call either takes a callback or uses promises.

    Based on that, I created two functions that wrap calls to .getItem and .setItem and inspects their responses. If they return an instance of Promise, it resolves using .then - otherwise executes the callback as normal:

    /**
     * Get a value from web storage regardless of whether it's sync or async
     */
    function getStorageItem(storage, key, callback) {
    
        if (typeof callback !== 'function') throw new Error('Invalid callback handler');
    
        var result = storage.getItem(key);
    
        if (result instanceof window.Promise) {
            result.then(callback);
        }
        else {
            callback(result);
        }
    }
    
    /**
     * Set a value in web storage regardless of whether it's sync or async
     */
    function setStorageItem(storage, key, value, callback) {
    
        var result = storage.setItem(key, value);
    
        if (result instanceof window.Promise && callback) {
            result.then(callback);
        }
        else if (callback) {
            callback();
        }
    }
    

    Which means I can now do:

    // get value from web storage
    getStorageItem(webStorage, key, function(value) {
    
        if (value) {
            // whatever I want with the value
        }
    });