The following example I have written shows how to
- Handle asynchronous HTTP calls;
- Wait for response from each API call;
- Use Promise pattern;
- Use Promise.all pattern to join multiple HTTP calls;
This working example is self-contained. It will define a simple request object that uses the window XMLHttpRequest object to make calls. It will define a simple function to wait for a bunch of promises to be completed.
Context. The example is querying the Spotify Web API endpoint in order to search for playlist objects for a given set of query strings:
["search?type=playlist&q=%22doom%20metal%22","search?type=playlist&q=Adele"]For each item, a new Promise will fire a block - ExecutionBlock, parse the result, schedule a new set of promises based on the result array, that is a list of Spotify user objects and execute the new HTTP call within the ExecutionProfileBlock asynchronously.
You can then see a nested Promise structure, that lets you spawn multiple and completely asynchronous nested HTTP calls, and join the results from each subset of calls through Promise.all.
NOTERecent Spotify search APIs will require an access token to be specified in the request headers:
-H "Authorization: Bearer {your access token}"So, you to run the following example you need to put your access token in the request headers:
var spotifyAccessToken = "YourSpotifyAccessToken";var console = { log: function(s) { document.getElementById("console").innerHTML += s +"<br/>" }}// Simple XMLHttpRequest// based on https://davidwalsh.name/xmlhttprequestSimpleRequest = { call: function(what, response) { var request; if (window.XMLHttpRequest) { // Mozilla, Safari, ... request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // Internet Explorer try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } } // State changes request.onreadystatechange = function() { if (request.readyState === 4) { // Done if (request.status === 200) { // Complete response(request.responseText) } else response(); } } request.open('GET', what, true); request.setRequestHeader("Authorization", "Bearer "+ spotifyAccessToken); request.send(null); }}//PromiseAllvar promiseAll = function(items, block, done, fail) { var self = this; var promises = [], index = 0; items.forEach(function(item) { promises.push(function(item, i) { return new Promise(function(resolve, reject) { if (block) { block.apply(this, [item, index, resolve, reject]); } }); }(item, ++index)) }); Promise.all(promises).then(function AcceptHandler(results) { if (done) done(results); }, function ErrorHandler(error) { if (fail) fail(error); });}; //promiseAll// LP: deferred execution blockvar ExecutionBlock = function(item, index, resolve, reject) { var url = "https://api.spotify.com/v1/" url += item; console.log( url ) SimpleRequest.call(url, function(result) { if (result) { var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) { return item.owner.href; }) resolve(profileUrls); } else { reject(new Error("call error")); } })}arr = ["search?type=playlist&q=%22doom%20metal%22","search?type=playlist&q=Adele"]promiseAll(arr, function(item, index, resolve, reject) { console.log("Making request ["+ index +"]") ExecutionBlock(item, index, resolve, reject);}, function(results) { // Aggregated results console.log("All profiles received "+ results.length); //console.log(JSON.stringify(results[0], null, 2)); ///// promiseall again var ExecutionProfileBlock = function(item, index, resolve, reject) { SimpleRequest.call(item, function(result) { if (result) { var obj = JSON.parse(result); resolve({ name: obj.display_name, followers: obj.followers.total, url: obj.href }); } //result }) } //ExecutionProfileBlock promiseAll(results[0], function(item, index, resolve, reject) { //console.log("Making request ["+ index +"] "+ item) ExecutionProfileBlock(item, index, resolve, reject); }, function(results) { // aggregated results console.log("All response received "+ results.length); console.log(JSON.stringify(results, null, 2)); } , function(error) { // Error console.log(error); }) ///// }, function(error) { // Error console.log(error); });<div id="console" />I have extensively discussed this solution here.