Skip to content

Instantly share code, notes, and snippets.

@cusster
Last active July 16, 2018 07:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cusster/bfba913b2c848afd74bc5374701bd0d0 to your computer and use it in GitHub Desktop.
Save cusster/bfba913b2c848afd74bc5374701bd0d0 to your computer and use it in GitHub Desktop.
function ChainedRequests(urls, headers) {
this.method = 'GET';
this.urls = urls || [];
this.headers = headers || {};
this.payload = null;
this.response = [];
this.retries = 1;
this.xhr = function(method, url, headers, payload) {
var self = this;
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest(),
info = {};
xhr.open(method, url);
if (headers instanceof Object && Object.keys(headers).length) {
for (var h in headers) {
xhr.setRequestHeader(h, headers[h]);
}
}
xhr.onerror = function() {
info.message = xhr.statusText;
info.url = xhr.responseURL;
reject(info);
};
xhr.onreadystatechange = function() {
// request is done
if (xhr.readyState == XMLHttpRequest.DONE) {
// resolve when response is 200
if (xhr.status == 200) {
// push response to array
self.response.push(xhr.responseText);
resolve(self.response);
}
// else, reject, so the chained request will terminate
else {
info.message = xhr.responseText;
info.url = xhr.responseURL;
/**
* if error is due to expired token,
* add the refreshed token to the argument
*
* e.g.
* info.token = xhr.getResponseHeader('Authorization')
*/
reject(info);
}
}
};
xhr.send(payload);
});
};
}
ChainedRequests.prototype = {
constructor: ChainedRequests,
send: function(completion) {
var self = this,
method = self.method,
urls = self.urls,
headers = self.headers,
payload = self.payload,
chain = Promise.resolve();
// create chained request to each URL
urls.forEach(function(url) {
chain = chain.then(function(res) {
return self.xhr(method, url, headers, payload);
});
});
if (completion instanceof Function) {
chain
// all requests were successfully called
.then(function(res) {
// final result has been resolved
completion.call(null, res);
})
// one of the request has failed
.catch(function(info) {
if (self.retries > 0) {
// decrement number of retries
self.retries--;
// get portion of the URLs where the request started to fail
var index = self.urls.indexOf(info.url);
if (index !== -1) {
self.urls = self.urls.slice(index);
}
/**
* setting of the newly refreshed token should be done here
*
* e.g.
* self.headers['Authorization'] = info.token
*/
self.send(completion);
} else {
// number of retries has been consumed
completion.call(null, null);
}
});
}
},
setMethod: function(method) {
this.method = method;
return this;
},
setPayload: function(payload) {
this.payload = payload;
return this;
},
setRetries: function(retries) {
this.retries = retries;
return this;
}
};
@cusster
Copy link
Author

cusster commented Jul 4, 2018

Sample usage:

var urls = [
        "https://api.test/resource1",
        "https://api.test/resource2",
        "https://api.test/resource3"
    ],
    headers = {
        'Authorization': '<token goes here>'
    };

var chained = new ChainedRequests(urls, headers);

// HTTP request method defaults to 'GET'
// to change method, use ChainedRequests.setMethod(String)
// to set payload for 'POST'/'PUT', use ChainedRequests.setPayload(mixed)
chained.send(function(res) {
    if (res !== null) {
        // all requests has been successful
        // 'res' is an array of response from the provided URLs
    } else {
        // failed request to at least one of the URLs after one retry (default)
        // to increase number of retries, use ChainedRequests.setRetries(Int)
    }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment