I have this code, wherein i'd like to make it in single array.
The output that data produce, is like this:
connections.elements.map((val: any) => {
const url = 'link'
return new Promise((resolve) => {
axios.post(url, val.firstName).then((res: { data: any }) => {
resolve(searchRequestBuilder(res.data.AllResults));
});
});
});
const searchRequestBuilder = async (data: any) => {
console.log(await data);
// for await (let resolvedPromise of data) {
// console.log(resolvedPromise);
// }
};
What I'd like to do is like this:
I already tried to make a variable and use .push, but it still doesn't combine in a single array. What was the thing I am missing?
So since your .map function will return an array of promises, you can store that in a variable, and you can use Promise.all or Promise.allSettled to wait for all the apis to resolve and you can get all the results in a single array in the order that it is requested.
Not part of the question, another thing is if you are not careful, and use await on each api request, it can make a waterfall pattern of requests, which means it will wait for API 1 to finish, then API 2 to finish.
But the way its written here, it will make all the requests parallely, so this will request all the APIs together, and once the last one gets resolved the Promise.all or allSettled will trigger its callback.
const searchRequestBuilder = async (data: any) => {
console.log(await data);
}
async function makeAPIrequests(){
let arrrayOfPromises = connections.elements.map((val: any) => {
const url = 'link'
return new Promise((resolve) => {
axios.post(url, val.firstName).then((res: { data: any }) => {
resolve(searchRequestBuilder(res.data.AllResults));
});
});
});
Promise.allSettled(arrrayOfPromises).
then((results) => console.log(results))
}
Edit:
Also in addition to this, I do not think that you need to return a new Promise inside the map funciton, if you just return axios it should work, since axios itself will return a promise.
The main problem there could be you are making a new call per each item of your elements array. This may create performance issues. The best practice may be to do only one async call and then get all items.
Anyway, if for some reason you really need to make all these ajax calls. Think you are working with asynchronous code, then you really don't know when all calls are answered. To fix this, you need to call your searchRequestBuilder after had made all calls.
I suggest you use Promise.all method.
I left you this link to show you how it works. Basically what you need to do is save all axios.post promises in an array and after loop your elements array call Promises.all passing the array with your promises, then you will be able to execute your searchRequestBuilder without problems.
You can try using .concat:
let allData = [];
allData = allData.concat(await data);
Related
I would like to get data from two API. in the firest call (getDatafromGeonames) I want to get latitude and longitude then I want to pass them as parameters to the second call (getWaetherData) to get weather information then I want to store the result and pass to my endpoint
Here is my try:
const res1 = getDatafromGeonames(geonameAPIURL,city,geonamesKey).then(res =>{
getWaetherData(res.address.lat, res.address.lng)})
Promise.all([res1]).then((res)=>{
//do somthing
console.log(res);
})
a screenshot of the result:
I don't know why it is jumping to then and printing Undefined before it is executing getWaetherData? How can I get data from the first API then use these data to get weather info then do something with these data
thanks a lot :)
Promise.all(promises[]) method is meant to execute some logic when all promises[] has been executed. This would be helpful if you have 2 independant async calls and you want to do something after both of them has been executed.
By what you have described, seems that the second API call you have to do needs the result of the first API call, so the promise operator you should use is .then().
const waetherData = getDatafromGeonames(geonameAPIURL,city,geonamesKey)
.then(res => getWaetherData(res.address.lat, res.address.lng))
or, in EMS6 style:
const getWaetherData = async (geonameAPIURL, city, geonamesKey) => {
const geodata = await getDatafromGeonames(geonameAPIURL,city,geonamesKey);
return await getWaetherData(geodata.address.lat, geodata.address.lng)
}
I have put this last code into a method because if you want to use await operator you need to be in a async context.
Promise.all only works for Promises you can run in parallel, you have to run them in sequence instead:
getDataFromGeonames(geonameAPIURL, city, geonamesKey)
.then((res) => getWeatherData(res.address.lat, res.address.lng))
.then((res) => {
// do something
console.log(res);
return res;
});
I am doing the following:
fetch("someurl")
.then(data => {return data.json()})
.then(resp => console.log(resp));
Now, usually i do the operations on resp from within the .then function, but would it be possible to assign resp to a variable, or at least store it somewhere so i can retrieve it in another function?
Example:
let thedata;
fetch(URL).then(res => {
return res.json()
}).then(data => {
console.log(data[0].category); //helloWorld
thedata = data[0].category
});
console.log(thedata);
function printsomething()
{return thedata}
Now thedata is going to be undefined, and i can't use the function printsomething without having it inside the .then() function.
This is what i meant by my question.
By assigning the fetch Promise chain to a variable, you can then call .then on that variable from multiple locations, though that's somewhat odd to do:
const prom = fetch("someurl")
.then(res => res.json());
prom.then((data) => {
console.log(data);
});
// Separately:
prom.then((data) => {
console.log(data.foo);
});
In most cases, it would make more sense to use your original strategy of putting everything inside a single .then after res.json().
Sure you can do that - but then you will need to track whether the async function has returned a value, which may be as simple a checking whether the value is defined. The variable could be in global scope, or a field in some object you import. But by not having the code that requires it called from within the then, it means wrapping use of that variable in a check, and handling the possibility it hasn't been set yet. Whether or not that is a good idea depends on context - best to avoid it if you can, but it's a pattern I have used on occasion.
No matter how you handle it you will need to either check or wait for the value so it's best to use it within the .then()
While it can be done if you need the variable outside of the .then() I think async/await is cleaner/easier to manage the flow by awaiting functions till they finish in the same way that .then would and ensuring your variable is available (you should still validate):
const someAPI = "https://jsonplaceholder.typicode.com/todos/1"
let data = null
const fetchFunc = async () =>{
const response = await fetch(someAPI)
data = await response.json()
// use data
}
const asyncFunc = async () => {
await fetchFunc()
console.log(data);
//use data
}
asyncFunc()
Basically I want to call an API twice, make an array [res1, res2] of the two responses, and then operate on this array. My code goes something like that:
function f() {
apiCall1(params1)
.then(response1 => [response1, apiCall2(params2)])
.then(data => someFunction(data))
}
Unfortunately, this method does not work. I get undefined properties of data[0] and data[1]. However, if I make only one API call, everything works fine. I am wondering whether my syntax is wrong and also what would be a good way to implement this? Thanks.
You can group promises with Promise.all, for example:
function f() {
Promise.all([apiCall1(params1), apiCall2(params2)])
.then(data => {
const response1 = data[0];
const response2 = data[1];
})
}
cf. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
I have a method like this:
doSomeRequests(listOfRequestParameters) {
let requests = listOfRequestParameters.map(parmeter => {
return axios.get(url + parmeter)
.then(data => {
const parameters= data.parameters;
return axios.delete(url, parameters)
})
})
return Promise.all(requests);
}
In this function I want to complete API requests for each element in a list (listOfRequestParameters), but for every element in the list I have to do two API requests.
The axios methods both return Promises.
The problem is that I do catch the result of the Promise returned y doSomeRequests method, but if one API requests fail I get an UnhandledPromiseRejectionWarning.
What am I missing? Is there a way to do this kind of Promise chain?
Thanks to #charlietfl for asking me if I really do doSomeRequests(..).then().catch(). Turns out I don't, I accidentally did doSomeRequests(..).then().then() but was somehow convinced the I need to search for the error in the "complex" Promise chain.
Anyway, I am sorry for the useless question, thank you for your help.
You need to make sure the map call returns a list of promises, simplifying with async/await you'll get something along the lines:
async doSomeRequests(listOfRequestParameters) {
return Promise.all(listOfRequestParameters.map(async parmeter => {
const data = await axios.get(url + parameter);
const parameters = data.parameters;
return axios.delete(url, parameters);
}));
}
await doSomeRequests(listOfRequestParameters);
I understand that this is a basic question, but I can't figure it out myself, how to export my variable "X" (which is actually a JSON object) out of "for" cycle. I have tried a various ways, but in my case function return not the JSON.object itself, but a "promise.pending".
I guess that someone more expirienced with this will help me out. My code:
for (let i = 0; i < server.length; i++) {
const fetch = require("node-fetch");
const url = ''+(server[i].name)+'';
const getData = async url => {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.log(error);
}
};
getData(url).then(function(result) { //promise.pending w/o .then
let x = result; //here is real JSON that I want to export
});
}
console.log(x); // -element is not exported :(
Here's some cleaner ES6 code you may wish to try:
const fetch = require("node-fetch");
Promise.all(
server.map((srv) => {
const url = String(srv.name);
return fetch(url)
.then((response) => response.json())
.catch((err) => console.log(err));
})
)
.then((results) => {
console.log(results);
})
.catch((err) => {
console.log('total failure!');
console.log(err);
});
How does it work?
Using Array.map, it transforms the list of servers into a list of promises which are executed in parallel. Each promise does two things:
fetch the URL
extract JSON response
If either step fails, that one promise rejects, which will then cause the whole series to reject immediately.
Why do I think this is better than the accepted answer? In a word, it's cleaner. It doesn't mix explicit promises with async/await, which can make asynchronous logic muddier than necessary. It doesn't import the fetch library on every loop iteration. It converts the server URL to a string explicitly, rather than relying on implicit coercion. It doesn't create unnecessary variables, and it avoids the needless for loop.
Whether you accept it or not, I offer it up as another view on the same problem, solved in what I think is a maximally elegant and clear way.
Why is this so hard? Why is async work so counterintuitive?
Doing async work requires being comfortable with something known as "continuation passing style." An asynchronous task is, by definition, non-blocking -- program execution does not wait for the task to complete before moving to the next statement. But we often do async work because subsequent statements require data that is not yet available. Thus, we have the callback function, then the Promise, and now async/await. The first two solve the problem with a mechanism that allows you to provide "packages" of work to do once an asynchronous task is complete -- "continuations," where execution will resume once some condition obtains. There is absolutely no difference between a boring node-style callback function and the .then of a Promise: both accept functions, and both will execute those functions at specific times and with specific data. The key job of the callback function is to act as a receptacle for data about the asynchronous task.
This pattern complicates not only basic variable scoping, which was your main concern, but also the issue of how best to express complicated workflows, which are often a mix of blocking and non-blocking statements. If doing async work requires providing lots of "continuations" in the form of functions, then we know that doing this work will be a constant battle against the proliferation of a million little functions, a million things needing names that must be unique and clear. This is a problem that cannot be solved with a library. It requires adapting one's style to the changed terrain.
The less your feet touch the ground, the better. :)
Javascript builds on the concept of promises. When you ask getData to to do its work, what is says is that, "OK, this is going to take some time, but I promise that I'll let you know after the work is done. So please have faith on my promise, I'll let you know once the work is complete", and it immediately gives you a promise to you.
That's what you see as promise.pending. It's pending because it is not completed yet. Now you should register a certain task (or function) with that promise for getData to call when he completes the work.
function doSomething(){
var promiseArray = [];
for (let i = 0; i < server.length; i++) {
const fetch = require("node-fetch");
const url = ''+(server[i].name)+'';
const getData = async url => {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.log(error);
}
};
promiseArray.push(getData(url)); // keeping track of all promises
}
return Promise.all(promiseArray); //see, I'm not registering anything to promise, I'm passing it to the consumer
}
function successCallback(result) {
console.log("It succeeded with " + result);
}
function failureCallback(error) {
console.log("It failed with " + error);
}
let promise = doSomething(); // do something is the function that does all the logic in that for loop and getData
promise.then(successCallback, failureCallback);