Download two URLs at once but process as soon as one completes - javascript

I have two API urls to hit. One known to be fast (~50-100ms). One known to be slow (~1s). I use the results of these to display product choices to the user. Currently I await-download one, then do the second. Pretty synchronous and because of that it's adding 50-100ms to the already-slow second hit.
I would like to:
Send both requests at once
Start processing data as soon as one request comes back
Wait for both requests before moving on from there.
I've seen the example Axios give...
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
But this appears to wait for both URLs to commit. This would still be marginally faster but I want the data from my 50ms API hit to start showing as soon as it's ready.

For sure you can chain additional .thens to the promises returned by axios:
Promise.all([
getUserAccount()
.then(processAccount),
getUserPermissions()
.then(processPermissions)
]).then(([userAccount, permissions]) => {
//...
});
wereas processAccount and processPermissions are functions that take the axios response object as an argument and return the wanted results.
For sure you can also add multiple .thens to the same promise:
const account = getUserAccount();
const permissions = getUserPermissions();
// Show permissions when ready
permissions.then(processPermissions);
Promise.all([account, permissions])
.then(([account, permissions]) => {
// Do stuff when both are ready
});
I replaced axios.all with Promise.all - I don't know why axios provides that helper as JS has a native implementation for that. I tried consulting the docs ... but they are not even documenting that API.

Related

can we set forkJoin() API request calling one after another, not all at once in angular

there is one scenario when i tried to make multiple update calls of same endpoint with different body in forkJoin(), server returning
"Transaction (Process ID 92) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction."
so API team asking to send requests one after another, for simplified and as per recommended by TL i'm using forkJoin([]), not for loop based.
can we configure a setting / solution to ask forkJoin(), call the array of Observable API endpoints, one after another,not all at once, Please Help.
let url = 'https://jsonplaceholder.typicode.com/users/';
if (this.inviteUpdateBatch.length) {
forkJoin(this.http.putAsync(`${url}`, data1),
this.http.putAsync(`${url}`, data2)).subscribe(()=> {});
}
You can simply use concat rxjs operator.
for example:
const e = of(1);
const s = of(2);
concat(e, s).subscribe(val => {
console.log('val', val);
});
this will execute the the requests one after the one.

Call stack size exceeded on re-starting Node function

I'm trying to overcome Call stack size exceeded error but with no luck,
Goal is to re-run the GET request as long as I get music in type field.
//tech: node.js + mongoose
//import components
const https = require('https');
const options = new URL('https://www.boredapi.com/api/activity');
//obtain data using GET
https.get(options, (response) => {
//console.log('statusCode:', response.statusCode);
//console.log('headers:', response.headers);
response.on('data', (data) => {
//process.stdout.write(data);
apiResult = JSON.parse(data);
apiResultType = apiResult.type;
returnDataOutside(data);
});
})
.on('error', (error) => {
console.error(error);
});
function returnDataOutside(data){
apiResultType;
if (apiResultType == 'music') {
console.log(apiResult);
} else {
returnDataOutside(data);
console.log(apiResult); //Maximum call stack size exceeded
};
};
Your function returnDataOutside() is calling itself recursively. If it doesn't gets an apiResultType of 'music' on the first time, then it just keeps calling itself deeper and deeper until the stack overflows with no chance of ever getting the music type because you're just calling it with the same data over and over.
It appears that you want to rerun the GET request when you don't have music type, but your code is not doing that - it's just calling your response function over and over. So, instead, you need to put the code that makes the GET request into a function and call that new function that actually makes a fresh GET request when the apiResultType isn't what you want.
In addition, you shouldn't code something like this that keeping going forever hammering some server. You should have either a maximum number of times you try or a timer back-off or both.
And, you can't just assume that response.on('data', ...) contains a perfectly formed piece of JSON. If the data is anything but very small, then the data may arrive in any arbitrary sized chunks. It make take multiple data events to get your entire payload. And, this may work on fast networks, but not on slow networks or through some proxies, but not others. Instead, you have to accumulate the data from the entire response (all the data events that occur) concatenated together and then process that final result on the end event.
While, you can code the plain https.get() to collect all the results for you (there's an example of that right in the doc here), it's a lot easier to just use a higher level library that brings support for a bunch of useful things.
My favorite library to use in this regard is got(), but there's a list of alternatives here and you can find the one you like. Not only do these libraries accumulate the entire request for you with you writing any extra code, but they are promise-based which makes the asynchronous coding easier and they also automatically check status code results for you, follow redirects, etc... - many things you would want an http request library to "just handle" for you.

Cannot get HTTP call response while debugging but it exists on the browser

I simply send an HTTP request to create a record and then get the added record id on the Network section of browsers that I tried Chrome, Firefox, but interestingly, I cannot get the same id value while debugging. Here is the code and the breakpoint that I am trying to get the response. I also tried to use promise, etc, but while it is working on other PC, I cannot get it on my PC (I also close antivirus app, etc. and clean browser histor, restart PC, restart the app, etc. But none of them make any sense.
this.myservice.create(<Employee>{ name: data.name, surname: data.surname })
.pipe(
tap((result) => {
// --> I am expecting to get result as id value as it
// is seen on the Network tab of Google Developer tool
...
Note that my service returns an observable and there is no problem at that sied. The problem is most probably related to browser or pc settings e.g. firewall or etc.
It might depend on the implementation of this.myservice.create. If that is implemented eager instead of lazy that will mean that your http request is already invoked before you subscribe, effectively already giving you the http response in the network inspector. But it will only get into the tap() as an emission once you subscribe() to your Observable which makes the whole stream active.
Example 'lazy' implementation:
create() {
return Observable.Create((obs) => {
fetch('http://example.com/movies.json')
.then(response => {
obs.onNext(response.json());
obs.onCompleted();
});
});
}
This will invoke the fetch only once the observable is subscribed to.
Example of an eager implementation:
create(){
const obs = new Rx.Subject();
fetch('http://example.com/movies.json')
.then(response => {
obs.next(response.json());
obs.complete();
});
return obs;
}
This will start the fetch even though the observable is not subscribed to already. It might even lead to a race condition (the value is already pushed before being subscribed to).
Having 'eager' observables is in my opinion often a mistake // anti-pattern because it can lead to very confusing results unless the engineers know intimately how the system works and it is documented throughly. And then still....

How to make promise.all wait for nested promise.all?

So I have a problem with JavaScript Promises. I'm using native implementation for the sake of reducing dependencies.
An illustrative example of what I need.
I need to retrieve lists of books, book authors and purchases.
I also need an author profile for each of the authors. After I got all of that, I need to create a nice set of Authors with their books and purchase list for each of the books.
Lists and profiles are separate API JSON calls. The only dependency is that I need a list of authors to be able to get author profiles.
I've solved this with Promises.
I use Promise.all to get 3 API JSON requests for: authors, books and purchases.
I use yet another Promise.all to get all the profiles for each of the authors I get (I loop through the list, map urls for each profile and send a batch of requests in parallel).
I run the profile request batch as soon as I get the list of authors, thus in the "Then" handler of the author list promise.
Now, the problem:
To be sure that all promises, 3 lists and all profiles, will be done prior to my assembling of the library set, I would need to send a profile batch of requests when I'm done with all the lists, in the first Promise.all Then handler.
But: lists of books an purchases take much longer time than the list of authors and I don't want to wait for all of those to send a batch of profile requests, so I send it in the Then handler of the author-list promise so these start as soon as I have the info.
However, a nested Promise.all does not count towards its parent Promise.all Then handler so since my FinalFunction is in the Then of the top-level Promise.all, it may (and sometimes does) fire before the nested Promise.all has finished retrieving all author profiles.
I need to be able to start all of the Promise requests as soon as possible, but only the batch of author requests depends on one promise being complete to start, so I need to wait on that one. All other should start independently.
Pseudo code
Promise.all(
requestBooks().then(){},
requestAuthors().then(){
GenerateArrayOfAuthorUris();
// now send a promisifyed batch of per-author requests
Promise.all(
[array of author uris to run requests for]
)
.then(){
// If I run this here, I may not have upper-level requests done
runCalculationsPerAuthorForAllAuthorsBooksPurchasesReceived();
}
},
requestPurchases().then(){},
)
.then(){
// this will fire when 3 top requests are done, but won't wait for
// the nested Promise.all with per-author requests
runCalculationsPerAuthorForAllAuthorsBooksPurchasesReceived();
}
If I do it this way, I'm wasting precious time by waiting for requests I don't need to wait on just to start per-author requests:
Promise.all(
requestBooks().then(){},
requestAuthors().then(){
GenerateArrayOfAuthorUris();
},
requestPurchases().then(){},
)
.then(){
// now send a promisifyed batch of per-author requests
Promise.all(
[array of author uris to run requests for]
)
.then(){
// If I run this here, I may not have upper-level requests done
runCalculationsPerAuthorForAllAuthorsBooksPurchasesReceived();
}
}
Hopefully this clarifies what I need.
Thank you.
This is the code sample: https://jsbin.com/qizohasofa/edit?js,console
As you were told in the comments, you didn't return anything from your functions, so then didn't know what inner promises to wait for.
function getJokeCategories() {
return Promise.all([
// ^^^^^^
pgetJSON("http://api.icndb.com/categories"),
pgetJSON("http://api.icndb.com/jokes/count").then(function(data) {
var jokesToGet = [];
for (var i=0; i<data; i++){
jokesToGet.push("http://api.icndb.com/jokes/"+i);
}
return Promise.all(jokesToGet.map(function(jk) {
// ^^^^^^
return pgetJSON(jk).then(function(joke) {
// ^^^^^^
console.log(jk + " just returned", joke);
return joke;
// ^^^^^^
});
})).then(function(jokes) {
console.log("All jokes returned. This does happen only when all jokes are retrieved.");
return {count:data, jokes:jokes};
// ^^^^^^
});
})
]);
}
getJokeCategories().then(function(result) {
console.log(result, "This does happen at the very end when joke count, joke categories and all jokes are returned.");
});

How to create an array with ajax and modify it without callback function?

I'm trying to use callbacks to get rid of the synchronous ajax calls in my code but I can't figure out how this could work. I'm using the spotify API to get all the artists in a playlist then perform tasks based on that information. The basic logic of the code is:
Get the user's playlist selections
Populate an array with the artist ids in those playlists
Make more ajax calls based on the array.
Use the array from step 3 to do another task.
The problem is that step 4 will come before step 2 and 3 if I don't set step 2 and 3 to synchronous. BUT I can't just call step three at the end of the step 2, and step 4 at the end of step 3 function because both occur in a while loop. Can't figure out a solution to this.
The calling function
This while loop goes through all a user's selections in a multiple selection box and calls the ajax function to append the data.
artistArray = [];
while (artistUrls[i] != null) {
getArtists(artistArray, artistUrls[i]);
i++;
}
doSomethingWithArtistArray(artistArray);
doAnotherThingWithArray(artistsArray);
The ajax function
Uses ajax calls to get the artist information and append it to an array
getArtists(artistArray, url) {
(if (url == null) {
return;
}
$.ajax({
async: false,
url: url,
headers: {
'Authorization': 'Bearer ' + access_token
},
error: function() {
console.log("Something went wrong with " + url);
return;
},
success: function(tracks) {
getArtists_Append(artists, frequencyArray, tracks); //Uses a while loop to append all the artist information to artistArray
},
});
//My idea was to call doSomethingWithArtistArray here but that's not working because there might be more calls to make.
console.log("finished getting artists");
return;
}
}
Get artists=
getArtists_Append {
while loop that populates the array
}
The problem is that you are treating your Ajax requests as if they were synchronous, when they are asynchronous (and you should do it like that to prevent blocking the browser).
The best approach is to:
In the specific case of fetching multiple artists from Spotify, use the endpoint for getting several artists. This will reduce the amount of requests you need to make to the Spotify's Web API.
If using callback functions, you will make an Ajax request. Then in its callback you will check if you need to make another Ajax request with the next chunk. If you don't need to make any other request because you are done, then call your next function, in this case doSomethingWithArtistArray.
If you are using Promises, then use Promise.all() passing an array of promises, where each promise wraps an Ajax request. This is useful when you already know what requests you need to make, and don't need the response from a request to figure out the next request to be made.
Have a look at the Code Examples section on the Spotify Developer Site to see some open source sites using the Web API.
For instance, you can see how the 2nd alternative is applied in Sort Your Music when getting playlists tracks. The function will make a request to the next chunk if there are more tracks to fetch, otherwise it won't.
For the 3rd alternative, since you are using jQuery you could use $.when to use promises. Check out this example. If you like the idea of promises and plan to make other requests to the Web API, I would recommend you using a wrapper like Spotify Web API JS (shameless self promotion). With that you could simply do:
var api = new SpotifyWebApi();
var promises = [];
promises.add(api.getArtists(['id1', 'id2', 'id3', 'id4', 'id5']));
promises.add(api.getArtists(['id10', 'id11', 'id12', 'id13', 'id14']));
Promise.all(promises).then(function(data) {
// data contains the result of the promises (ajax requests)
// do something with it
});

Categories

Resources