array of callbacks as Promise?

135 Views Asked by At

I am working through some old legacy code dealing with network requests using RPC/YUI library. It essentially creates tags to handle network requests. There are no promises for these.Also, because of IE11 support, we cannot use the native Promise object. Our build process does not utilize any NPM dependencies, so we cannot use any babel related polyfills.

There is a bug I am working on to fix that the argument ignoreError gets overwritten each time another function calls the same function....obviously! We have multiple functions calling this network request function library. Sometimes we want to ignore an error, sometimes we do not.

What is the ideal way to store the multiple requests made and their respective error callbacks so the appropriate item is called?

example:

var rpcUrl,
rpcRetries,
rpcIgnoreError;

// main function that sets some globals:
rpc: function(url, retries, ignoreError) {
  rpcUrl = url;
  rpcRetries = retries;
  rpcIgnoreError = ignoreError;
  this.doRpc();
},
// calls the YUI library to initialize network script:
doRpc: function() {
  YAHOO.util.Get.script(rpcUrl, {
    onFailure: function() {
      this.callbackError(true);
    },
    timeout: 55000
  });
},
// YUI callback
callbackError: function(retry) {
  if (retry && rpcRetries > 0) {
    rpcRetries = rpcRetries - 1;
    this.doRpc();
  } else {
    // ** how do i know this error handling is for the script which failed?
    if (!rpcIgnoreError) {
      this.populateFormStatus(6);
    }
  }
},

now, we have multiple functions calling rpc() such as:

sendConfig: function() {
  this.rpc(urlForEndpoint, 3, true);
},
sendUser: function() {
  this.rpc(urlForEndpoint, 3, false);
},
sendWidget: function() {
  this.rpc(urlForEndpoint, 3, false);
},

I am concerned making an array of callbacks will not guarantee that each item is handled with its respective handler.

I could do something like create a map constant:

var RPC_ERR_CB = {
    sendConfig: false,
    sendUser: true,
    sendWidget: true
};

// and then in the onFailure callback, I can read the src of the script tag:

...
doRpc: function() {
  YAHOO.util.Get.script(rpcUrl, {
    onFailure: function() {
      var hasCB = Object.keys(RPC_ERR_CB).some(function(item) {
        return arguments[0].src.indexOf(RPC_ERR_CB[item]) <= 0;
     });
      if (hasCB) {
        this.callbackError(true);
      }
    },
    timeout: 55000
  });
},

Hope this makes sense...THANKS!

2

There are 2 best solutions below

0
MattB On

You could pass the values into doRpc, then you can pass it to callbackError or handle it in doRpc (like your example code at the end). This will prevent the global variable from changing on you.

0
trobinson On

If you're not able to use Promises or ES6 Classes, your options become somewhat limited. If at all possible, I would recommend biting the bullet on getting a Babel transpilation process so you can take advantage of newer features without needing to drop IE11 support.

As it is now though, ideally you don't want to track every request in a global variable somewhere. You can handle each transaction independently by creating each request as a self-contained object:

function RpcRequest (url, retries, ignoreError) {
  this.url = url
  this.retries = retries
  this.ignoreError = ignoreError
}

RpcRequest.prototype.send = function() {
  YAHOO.util.Get.script(this.url, {
    onFailure: function() {
      this.callbackError(true);
    },
    timeout: 55000
  });
}

RpcRequest.prototype.callbackError = function(retry) {
  if (retry && this.retries > 0) {
    this.retries = this.retries - 1;
    this.send();
  } else {
    if (!this.ignoreError) {

      // ...

    }
  }
}

// Somewhere else, initiate a request
var requestOne = new RpcRequest("http://blah", 3, false)
requestOne.send()

Something I noted when looking over your code: the code that's creating the request has no idea whether the request succeeded or not. And when you have an error, the calling context doesn't know anything about that error. I took a look at the library you mentioned, and it does appear to have some context that you can pass along.

If I were to rewrite this a little bit, I'd do something like this to bubble the error up to your calling context:

RpcRequest.prototype.send = function(callback) {
  YAHOO.util.Get.script(this.url, {
    onFailure: function(context) {
      if( this.ignoreError ) {
        context.ignoredError = true
        callback(null, context);
        return;
      }

      var retError = new Error('Failure doing something!');
      retError.context = context;

      callback(retError);
    },
    onSuccess: function(context) {
      callback(null, context);
    },
    timeout: 55000
  });
}

// Somewhere else in the code...
sendWidget: function() {
  var request = new RpcRequest(urlForEndpoint, 3, false)
  request.send(function(err, result) {
    if( err ) {
      console.error('Failed at doing a widget thing:', err.context);
      // maybe even:
      // throw err;
      return;
    }

    if( result.ignoredError ) {
      console.warn('Ignored an error on the widget thing:', result);
      return;
    }

    console.log('Success on the widget thing!', result);
  })
}