Resolve promise when map request are finished - javascript

I am working on working on an bookmark page, we get the bookmark results with the bookmarked restaurant id. Then I map the response and push it as objects to an array.
I want to resolve the whole finished array so I can manipulate the data afterwards.
I have created an getData function which does a request to the bookmark api, in the onSucces I call a function called mapResult which looks like this:
mapResults(result: Array<any>, type: string): Promise<any> {
const promise = new Promise<any>((resolve, reject) => {
const requests = result.map((res, i) => {
this.getRestaurantById(res.RestaurantId).then(restaurant => {
const bookmarks = {
createdDate: res.CreatedDate,
restaurant: restaurant[0]
};
this.savedData[type].push(bookmarks);
});
});
Promise.all(requests).then((completed) => {
if(completed) {
console.log(completed)
resolve(this.savedData[type]);
}
})
});
return promise;
}
Where I subscribe to like this:
this.mapResults(result, type).then(data => {
console.log(data)
});
But the data console.log is not the whole data array, it will just resolve the first object.
Why does the Promis.all function not wait for the map to finish?

You have several problems in your code:
You don't return anything in the result.map callback
new Promise is not necessary
You should not use a state variable instead of the promise return value
The completed variable does not make any sense
This code should work as expected:
mapResults(result: Array<any>, type: string): Promise<any> {
// you don't need "new Promise" as "getRestaurantById" already returns a promise itself
const requests = result.map((res) => {
// here you forgot to return something
return this.getRestaurantById(res.RestaurantId).then(restaurant => {
return {
createdDate: res.CreatedDate,
restaurant: restaurant[0]
};
});
});
// using "completed" did not make any sense as it is just an array filled with "undefined"s
return Promise.all(requests).then((restaurants) => {
console.log(restaurants)
// TODO store "restaurants" in "this.savedData[type]" if needed
return restaurants;
});
}

You don't return your promise in the map.

Related

Typescript promise not resolving correctly before moving on in async code

I am trying to populate the 'documents' object which is just a Documentation array. I first build out a list of Promise and store the values in 'promises' to then call Promise.all on to fire things all off at once. Then for each promise, I try and grab the text value from each response in resolvePromise(), create a document from the value, and push it to documents.
type Documentation = { name: string; source: string; rawText: string };
const documents: Documentation[] = [];
async function resolvePromise(entry: Response, documents: Documentation[]) {
const document = { name: '', source: '', rawText: '' };
document.rawText = await entry.text(); // entry.text(): Promise<string>
documents.push(document);
console.log('documents length is now: ' + documents.length);
}
async function createDocumentsObject() {
const promises: Promise<Response>[] = [];
// code to populate promises list
HELP.forEach((value: ConfigItem[], key: string) => {
value.forEach((configElem: ConfigItem) => {
if (!configElem.subsection) {
const promise = getRequest(configElem.source);
promises.push(promise);
}
});
});
console.log('promises: ');
console.log(promises); // const promises: Promise<Response>[]
await Promise.all(promises).then(async values => {
values.forEach(async entry => {
if (!entry.ok) {
return Promise.reject();
} else {
return await resolvePromise(entry, documents);
}
});
});
console.log('docs');
console.log(documents);
}
In the print statement below, you can see the promises variable is populated with the promises correctly. However, the call to console.log(documents); runs before the calls to resolvePromise(). I tried using await entry.text(); to get the string value from entry.text() before moving on, but that is not working. I am trying to populate the documents object immediately for use right after in the code. I am new to TS so noob friendly explanations are appreciated!
The problem is here:
values.forEach(async entry => {...})
forEach will not resolve the promises returned by the async function.
I would change it to
return Promise.all( values.map( async entry => {...} ) )

Promise.All returning empty

I have few api call requests which i am trying to create promises and then execute all using Promise.all but, its giving empty value instead of array. Below is my code.
function getUser(arrUser) {
var student = [];
return new Promise((resolve, reject) => {
if (arrUser.length > 0) {
var promises = arrUseridRequest.map(userRequeset => {
return getRequest(userRequeset).then(result => {
student.push(JSON.parse(result.body).result[0].Name);
console.log(student); //This is giving right details.
}).catch(error => {
reject(error);
});
});
Promise.all(promises).then(StuName => {
resolve(StuName.join());
})
}
});
}
and this is how i am trying to get the values at once:
getUser('123').then(student => {
console.log(Student) //Coming as empty
});
getRequest is my api call nodeJs request module. What's wrong in my code?
All your promises fulfill with the value undefined since you're just logging the student names, but not returning them from the then callback. As you seem to be doing only a single request, the array will be [undefined], which is joined into the empty string.
Also avoid the Promise constructor antipattern:
function getUsers(arrUser) {
const promises = arrUser.map(userId => {
return getRequest(userId).then(result => {
const students = JSON.parse(result.body).result;
return students[0].Name;
});
});
return Promise.all(promises);
}
getUsers(['123']).then(studentNames => {
console.log(studentNames);
console.log(studentNames.join());
}).catch(console.error);

Synchronously populate/modify an array and return the modified array inside a promise

I'm pretty new to ReactJS and redux, so I've never really had to work with this before. I'm retrieving data from an API call in my project. I want to modify the data by adding a new property to the object. However, because the code is not ran synchronously, the unmodified array is being returned (I assume) instead of the modified array.
export function loadThings() {
return dispatch => {
return dispatch({
type: 'LOAD_THINGS',
payload: {
request: {
url: API_GET_THINGS_ENDPOINT,
method: 'GET'
}
}
}).then(response => {
let things = response.payload.data;
// Retrieve all items for the loaded "things"
if(things) {
things.forEach((thing, thingIndex) => {
things[thingIndex].items = []
if (thing.hasOwnProperty('channels') && thing.channels) {
thing.channels.forEach(channel => {
if (channel.hasOwnProperty('linkedItems') && channel.linkedItems) {
channel.linkedItems.forEach(itemName => {
dispatch(loadItems(itemName)).then(
item => things[thingIndex].items.push(item) // push the items inside the "things"
)
})
}
})
}
})
}
things.forEach(data => console.log(data.items.length, data.items)) // data.items.length returns 0, data.items returns a populated array
return things // return the modified array
}).catch(error => {
//todo: handle error
return false
})
}
}
As you can see, I perform an API call which returns data named response. The array is populated with all "things". If things exists, I want to load extra information named "items". Based on the information in the things array, I will perform another API call (which is done by dispatching the loadItems function) which returns another promise. Based on the data in the results of that API call, I will push into the items property (which is an array) of the things object.
As you can see in the comments, if I loop through the things array and log the items property which I just created, it's basically returning 0 as length, which means the things array is being returned before the things array is being modified.
I would like to know two things:
What is causing my code to run async. Is it the
dispatch(loadItems(itemName)) function since it returns a promise?
How am I able to synchronously execute my code?
Please note: this function loadThings() also returns a promise (if you're not familair with redux).
You might be interested in knowing what I tried myself to fix the code
Since I fail to understand the logic why the code is ran async, I've been trying hopeless stuff. Such as wrapping the code in another Promise.all and return the modified array in that promise. I used the then method of that promise to modify the things array, which had the exact same result. Probably because return things is being executed outside of that promise.
I'd really love to know what is going on
Edit
I have added the loadItems() code to the question, as requested:
export function loadItems(itemName) {
return dispatch => {
const url = itemName ? API_GET_ITEMS_ENDPOINT + `/${itemName}` : API_GET_ITEMS_ENDPOINT;
return dispatch({
type: 'LOAD_ITEMS',
payload: {
request: {
url: url,
method: 'GET'
}
}
}).then(response => {
return response.payload.data
})
}
}
My approach would be to map over things, creating arrays of promises for all of their items wrapped in a Promise.all, which gives you an array of Promise.all's.
Then you return things and this array of promises in another Promise.all and in the next then block, you can just assign the arrays to each thing with a simple for loop:
export function loadThings() {
return dispatch => {
return dispatch({
type: 'LOAD_THINGS',
payload: {
request: {
url: API_GET_THINGS_ENDPOINT,
method: 'GET'
}
}
}).then(response => {
let things = response.payload.data;
// Retrieve all items for the loaded "things"
const items = things.map((thing) => {
const thingItems = []
if (thing.hasOwnProperty('channels') && thing.channels) {
thing.channels.forEach(channel => {
if (channel.hasOwnProperty('linkedItems') && channel.linkedItems) {
channel.linkedItems.forEach(itemName => {
thingItems.push(dispatch(loadItems(itemName)));
});
}
});
}
return Promise.all(thingItems);
});
return Promise.all([things, Promise.all(items)])
})
.then(([things, thingItems]) => {
things.forEach((thing, index) => {
thing.items = thingItems[index];
})
return things;
})
.catch(error => {
//todo: handle error
return false
})
}
}
Edit:
You need to push the dispatch(loadItmes(itemName)) calls directly into thingItems.
I guess you could refactor it like the following:
export function loadThings() {
return dispatch => {
return dispatch({
type: 'LOAD_THINGS',
payload: {
request: {
url: API_GET_THINGS_ENDPOINT,
method: 'GET'
}
}
}).then(response => {
let things = response.payload.data;
// Retrieve all items for the loaded "things"
if( things ) {
return Promise.all( things.reduce( (promises, thing) => {
if (thing.channels) {
thing.items = [];
promises.push( ...thing.channels.map( channel =>
channel.linkedItems &&
channel.linkedItems.map( item =>
loadItems(item).then( result => thing.items.push( result ) )
) ).flat().filter( i => !!i ) );
}
return promises;
}, []) );
}
return things;
}).catch(error => {
//todo: handle error
return false
})
}
}
In case you would have things, it would check for the channels and the linkedItems for that channel, and create a promise that will push the result back to the thing.items array.
By returning the Promise.all, the continuation of the loadThings would only complete when the Promise.all was resolved. In case there are no things, just things gets returned (which would be a falsy value, so I am wondering how valid that statement could be)
I haven't actually tested the refactoring so there might be some brackets in need of adjusting to your situation, but I guess it gives you an idea?

Export the return value of chain promise

How to export the returned value of a multiple promise. I need to export both data from first API to second API data. Which means insert first data to an array and also the second then return that array and export to use from another .js file.
I tried to search on how can I returned the value inside then() method. But when I call the imported file into another .js file it logs [Promise, Promise, Promise...]
API.js
function getAllData(url) {
return axios.get(url, {
headers: {
"Accept": "application/json; odata=verbose"
}
}).then(response => response.data.d.results);
}
function getAllDataVHistory(data) {
return data.map(i => axios.get(`urltwo?getID${i.Id}`).then(response => {
return response.data;
}));
}
export const final = () =>
getAllData(`urlone`)
.then(data => getAllDataVHistory(data));
Display.js
import {
final
} from "./API.js";
final().then(en => {
// return new Promise((resolve, reject) => {
// if (true) {
// return resolve(console.log(en));
// } else {
// return reject("promise failed");
// }
// });
console.log(en);
});
Console.log result
(3) [Promise, Promise, Promise]
return data.map(i => axios.get(`urltwo`)
This is going to return an array of promises
Since you are resolving a promise with that, you are going to get that array (hence what you see in the logs).
You need to return a single promise so it will be adopted.
Use Promise.all(array_of_promises) to create a single promise that will resolve when all the promises in the array have resolved.

JavaScript | Access the object of a Promise from another Promise

In my AngularJS Application, I want to access the return value of a Promise, which I access from a service outside of my controller and import it there. This Promise, return an object. I want to access that objects and properties inside it.
I created the service to get that endpoint. Look below:
export const getEndpoints = () => {
const options = {
method: httpMethod.GET,
url: endpoint.ENVIRONMENT,
};
return Instance(options);
};
The above service in return reads an endpoint which I provide and uses axios on the background. This part is working just fine.
Then imported it, on my angular Controller:
import { getEndpoints } from './config/service';
Finally I created this function:
$scope.isItAvailable = false; // I will use this later to check the actual value. It is not important in the scope of the question..
const checkIfItIsAvailable = () => {
return new Promise((resolve, reject) => {
resolve(getEndpoints)
console.log(getEndpoints)
})
}
// And in my main function I am just calling the above
const mainFn = () => {
checkIfItIsAvailable()
// Along with a few others..
}
Actual Results
Now, in my console, I get the functioncheckIfItAvailable printed out.
Expected Results
I instead want to print to the console, the actual value that is being returned by the original promise, the object, and its properties.
Probably You need to call that function instead of just passing it as a parameter.
const checkIfItIsAvailable = () => {
return new Promise((resolve, reject) => {
resolve(getEndpoints()) // here
console.log(getEndpoints())
})
}
Then to get that resolved later in your main function, or wherever - just use then:
const mainFn = () => {
checkIfItIsAvailable().then((result) => {
// do what you need with result here
// $scope.isItAvailable = result probably like this
});
}
Please comment if you need another result. I see at least this issue at the moment.
Also, here is a snipper, illustrating that you need to call it instead of just passing.
// here is an example function which just return some string
function getSomething() {
return 'something'; // it could be a promise also
}
// here is example without calling functions, but just passing:
const promise = new Promise((resolve, reject) => {
console.log('NO CALL: ', getSomething);
});
// here is example with calling, so value is resolved
const promise2 = new Promise((resolve, reject) => {
console.log('CALLED: ', getSomething());
});
Here, getEndpoints is an asynchronous function which returns a Promise, and the way to get a return value from promise is to use then callback. You can do it like this:
const checkIfItIsAvailable = () => {
return new Promise((resolve, reject) => {
getEndpoints().then(resultOfEndPoint => {
console.log(resultOfEndPoint);
resolve(resultOfEndPoint);
});
})
}
It's possible to access the resolved result of getEndpoints in checkIfItIsAvailable calling getEndpoints() and using then() function:
const checkIfItIsAvailable = () => {
return new Promise((resolve, reject) => {
// call getEndpoints
getEndpoints()
// call then to get a result from getEndpoints
.then(res => {
console.log(res);
// checkIfItIsAvailable will be resolved with
// the result of getEndpoints
resolve(res);
});
});
}

Categories

Resources