0

I'm building a "storage provider" that allows consuming code to store stuff through an interface. Consider the below code snippets to be pseudocode as I'm going for MCVE. I'm trying to get my hands on IMPORTANTDATA and IMPORTANTKEY below.

At the lowest level, I have a baseService:

define([], function(){
    return function(){
        this.sendRequest = function(data){
              return $.ajax(data).done(function(response){
                  return response.IMPORTANTDATA; //          <---- This is needed
              }).fail(function(response){
                  throw new Error(response);
              }); 
        }
    }
})

I build services with this to reuse some base functionality, for example - eventService:

define(["baseService"], function(baseService){
      const eventService = new baseService();
      eventService.postMediaEvent = function(eventType, mediaPath, storageProvider){
           // isolated logic here
           return eventService.sendRequest(someData);
      }
})

This is where things start to get tricky: I have a baseStorageClient:

define(["eventService"], function (eventService) {
     return function(){
          this.storageProvider = null;
          const self = this;
          this.storeMetadata = function(eventType, mediaPath){
              return eventService.postMediaEvent(eventType, mediaPath, self.storageProvider);
          };

          this.storeMedia = function(){
              throw new Error("Not Implemented");
          };
     }
}

But this guy isn't ever used directly. I have instances of this created - for example, indexedDbClient:

define(["baseStorageClient"], function(baseStorageClient){
    const indexedDbClient = new baseStorageClient();

    indexedDbClient.storeMedia = function(blob){
         return openDatabase().then(function () {
             const request = database.transaction(storeName, "readwrite")
                .objectStore(storeName)
                .add(dbEntry);

             request.onsuccess = function (event) {
                 logger.log("combined segments saved into database.");
                 // todo - figure out how to resolve here
                 return {
                    IMPORTANTKEY: dbEntry.mediaId //          <---- This too
                 }
              };

              request.onerror = function (event) {
                  // todo: figure out how to reject here
                  logger.log("Unable to save segments " + e);
              };
         });
    }
})

And this client is used within my storageInterface:

define(["indexedDbClient"], function(indexedDbClient){
    const storageInterface = {};
    var currentClient = indexedDbClient; // might be other clients
    storageInterface.storeMedia = function (blob) {
        return currentClient.storeMedia(blob).then(function(mediaPath) {
            return currentClient.storeMetadata(eventType, mediaPath);
        });
    }
});

This is where things get super hairy. What I'm trying to achieve is the following:

 storageInterface.storeMedia(superBuffer).then(function (importantStuff) {
       // this should go storeMedia > baseStorageClient > eventService
       importantStuff.IMPORTANTKEY;
       importantStuff.IMPORTANTDATA;         
 });

But I can't quite figure out how to get this handled. How can I compile a result along a chain of promises like this?

SB2055
  • 12,272
  • 32
  • 97
  • 202

1 Answers1

1

There's two major problems:

  • You should treat done and fail as deprecated. They don't allow for any chaining, they will discard the results of the callback. Always use then.

    sendRequest = function(data){
        return $.ajax(data).then(function(response){
            return response.IMPORTANTDATA;
        }, function(response) {
           throw new Error(response);
        });
    }
    
  • Your transaction doesn't return any promise yet, so there's nothing for you to chain onto. You'll need to promisify it first:

    function promiseFromRequest(req) {
        return new Promise(function(resolve, reject) {
            req.onsuccess = resolve;
            req.onerror = reject;
        });
    }
    

    Now you can actually use it like so:

    storeMedia = function(blob){
        return openDatabase().then(function () {
            return promiseFromRequest(database.transaction(storeName, "readwrite")
            .objectStore(storeName)
            .add(dbEntry))
            .then(function (event) {
                logger.log("combined segments saved into database.");
                return {
                    IMPORTANTKEY: dbEntry.mediaId
                }
            }, function (e) {
                logger.log("Unable to save segments " + e);
                throw e;
            };
        });
    };
    

With those, you should be able to combine the results from storeMedia and storeMetaData in some way.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Awesome! I didn't know I could use `then` to pass `success` and `failure` callbacks - will update my code. How would I handle `always` - just move to another function and invoke 2x? – SB2055 Jun 01 '17 at 01:14
  • @SB2055 That's the simplest, yes, or use [`finally`](https://stackoverflow.com/a/32362233/1048572) – Bergi Jun 01 '17 at 01:44