How to add properties to object incrementally? - javascript

I am trying to map object where correct ID will contain the array that belongs to it.
Here is the scenario:
let obj = {}
const arr = []
for (let i = 0; i < someIds.length; i++) {
const res = await someApiCall() // returns array
obj = {
id: someIds[i],
res
}
arr.push(obj)
return arr
}
The thing here is that arr will only output last set of object because of iterating over the values.
For example I get
[{id: 122, res: "whatever122"}]
where I want to get
[{id: 122, res: "whatever122"}, {id: 111, res: "whatever111"}, {id: 133, res: "whatever133}]
How do I concatenate all?

You return arr to fast. It should be returned outside the for scope:
let obj = {}
const arr = []
for (let i = 0; i < someIds.length; i++) {
const res = await someApiCall() // returns array
obj = {
id: someIds[i],
res
}
arr.push(obj)
}
return arr

Move the return statement outside of the for loop, which would wait for all of the objects to get added to the array.
let obj = {}
const arr = []
for (let i = 0; i < someArr.length; i++) {
const res = await someApiCall() // returns array
obj = {
id: res[i],
res
}
arr.push(obj)
}
return arr

The problem is produced by the return statement that should probably stay outside the for loop. It completes the execution of the current function and returns (an optional value) to the caller.
A simpler way to write what you need is to use Promise.all() to run all the calls to someApiCall() in parallel (if they are independent). This way the code run much faster:
async function doSomething() {
return await Promise.all(
someIds.map(
async (id) => {
return {
id: id,
res: await someApiCall(id),
};
}
)
);
}
How it works
someIds.map() calls the callback function that it receives as argument for each item of someIds and returns a new array that contains the values returned by the callback function.
Because the callback function is asynchronous and uses await, it returns a Promise as soon as the await-ed call starts, without waiting it to complete.
The array of promises returned by someIds.map() is passed as argument to Promise.all() that waits for all of them and returns an array containing the values returned by the promises (which are the objects returned by the callback were they be synchronous).
Remark
The await in front of Promise.all() is not really needed. Without it, the function doSomething() returns the promise returned by Promise.all(). With it, the function returns an array of objects. Either way, the call of doSomething() still needs to be await-ed and the value produced by it after await is an array of objects.

Related

call function in object parameter - await fetch to change object element

I don't understand why it returns each fruit with the same size. It's like it returns only the fruit size of the first fruit / first fetch result. I mean allFruits has always the same size, thats not correct. And make an await in the object element like { name: await getFruitName(f.1)} does not work. Maybe the function should be called in another place?
const food = await fetchFood();
food.entries.map(await fruits)
var allFruits = {}
async function fruits(f) {
allFruits=
{
name: getFruitName(f.1)
}
let fruitSize = await FetchFruitsSize(f.2)
allFruits.size = fruitSize.data[0].sizes[1]
}
return allFruits
If somebody has an example how to change the object value for each array element with calling a function that is calling a fetch, I would be very greateful
Some issues:
allFruits should be an array, and the individual objects that your code creates should become entries in that array, so it becomes an array of objects.
The async fruits callback returns a promise that resolves to undefined, because there is no return statement.
In map(await fruits) it makes no sense to use await for a function object (which is not a promise). Just drop that await keyword.
food.entries.map(fruits) returns an array of promises, but your code does not await those promises. You should feed them to Promise.all and await that combined promise to resolve.
f.1 and f.2 are not valid syntax: if 1 and 2 are supposed to be properties of f, then you need square brackets. But it doesn't look like f is an array, so I have just dropped those syntax issues in below proposed code: adapt as needed.
The above points are resolved in this version:
(async function () {
const food = await fetchFood();
const allFruits = await Promise.all(food.entries.map(fruits));
// Do something with the fruits...
console.log(allFruits);
async function fruits(fruit) {
let fruitSize = await FetchFruitsSize(fruit); // adapt to do f.2
return {
name: getFruitName(fruit), // adapt to do f.1
size: fruitSize.data[0].sizes[1]
};
}
})();
There might still be other issues that relate to what the functions return that are called here... including: fetchFood, FetchFruitsSize, and getFruitName.

How can I push elements out of a forEach() context?

This is my first post ever :)
So what I am trying to do is loop through keys of an object with a forEach() loop and for each element, push specific values into an array. Then after the loop, resolve the array containing all pushed values.
As I understand, it's hard to get out values of the forEach() context. Is there a way to do something like that ?
Here's a code exemple of what I'm trying to do:
some function returning a promise
...
...
let promisesArray = [];
//Loop on each object from data and do whatever
ObjectJSON.array.forEach((object) => {
if (object.key === "foo") {
functionBar()
.then((response) => {
promisesArray.push(
`Object: ${object.key} | ${object.someOtherKey}`
);
})
.catch((error) => {
reject(error);
});
}
});
resolve(promisesArray);
}); //end of promise's return
Right now, it returns an empty array.
The expected result should be something like this instead:
[
'Object: key1 | someOtherKey1',
'Object: key2 | someOtherKey2',
'Object: key3 | someOtherKey3',
...
]
Thanks a lot for your answers !
Congrats on your first post ever!
It's a super classic question though, how to use an asynchronous function in a loop. With .forEach you can't, but you can using await in a for loop :
let promisesArray = [];
for (let object of ObjectJSON.array) {
if (object.key !== "foo") continue;
const response = await functionBar();
promisesArray.push(
`Object: ${object.key} | ${object.someOtherKey}`
);
}
resolve(promisesArray);
Using await will probably require your function to be marked as async (async function doSomething() { ... )

How to invoke async/await spread across multiple functions?

I was following along the MDN article on async/await and understanding it fairly well and then
I had a brain fart, and I'm not sure what's going on now. This example is an MDN example from the article on async/await.
async function makeResult(items) {
let newArr;
for (let i = 0; i < items.length; i++) {
newArr[i].push('word_' + i);
}
return newArr;
}
async function getResult() {
let result = await makeResult(["1", "2"]);
console.log(result);
}
How do I get this code to log the result to the console? I get errors when trying to call these functions from the main flow of the program. What am I supposed to do? I'm familiar with Promises, .then() chaining, and how to use async await in one basic async function, but not sure what to do here.
Edit: The problem was that newArr wasn't initialized as an empty array, and that I was pushing to newArr[i] instead of newArr. This is all very strange since the code snippet on https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await was written incorrectly.
If a function is asynchronous but doesn't do any async work, is it really asynchronous? If a tree falls in the woods...
The first function would return aPromise<Array<string>> if the code in it worked, the second function would return a Promise<undefined> because there's no return. You can see this by commenting out the first line in the second function body, then console.log(getResult()). The first function has logic problems (newArr is undefined, but it looks like the loop wants it to be a 2d array).
To log the result of an async function to the console, you can do what you already know (using .then), or await it in another async function:
const foo = async () => true
// this works
foo().then(console.log)
// this also works
;(async () => {
const bool = await foo()
console.log(bool)
})()
You have some problems in your code.
1 - Your newArr is undefined since you haven't initialized it.
2 - You are trying to push into an array that is in newArr[i] instead of pushing into newArr. Since there is nothing in newArr, the push returns undefined.
Async works just the way you have written here. You can combine the promises to wait for each other. And, the return statement will convert into a promise.
For example, the following:
async function foo() {
return 1
}
...is equivalent to:
function foo() {
return Promise.resolve(1)
}
Looking at your code,
async function makeResult(items) {
let newArr= [];
for (let i = 0; i < items.length; i++) {
newArr.push('word_' + i);
}
return newArr;
}
async function getResult() {
let result = await makeResult(["1", "2"]);
console.log(result);
}
getResult();
The problem with the code you've shown is that you're wrongfully using the array.
You need to initialize with an empty array:
let newArr = [];
In your loop, you're trying to use push on non-existent array-elements. Instead you want to use it on the array itself:
newArr[i].push('word_' + i);
async function makeResult(items) {
let newArr = [];
for (let i = 0; i < items.length; i++) {
newArr.push('word_' + i);
}
return newArr;
}
async function getResult() {
let result = await makeResult(["1", "2"]);
console.log(result);
}
getResult();
As it turns out, MDN has this exact faulty code example. I've submitted an issue for it: https://github.com/mdn/content/issues/1202
just do it like below, because you did wrong in makeResult(items) function array declaration/use block:
function makeResult(items) {
let newArr = [];
for (let i = 0; i < items.length; i++) {
newArr.push('word_' + i);
}
return newArr;
}
async function getResult() {
let result = await makeResult(["1", "2"]);
console.log(result);
}
//testing
getResult()

How to return correct array length after all the elements have been pushed in async/await used in map()? [duplicate]

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 2 years ago.
When I use the map object to push the results of async/await to all elements in an array, I want to return after all the elements have been pushed.
Here's a sample code:
const obj = {
'a': '1234',
'b': '5678'
}
const balances = []
Object.keys(obj).map(async (key, index) => {
const r = await getBalance(obj[key]) // async/await function to get balance of user id
balances.push(Math.floor(r))
console.log(balances.length)
})
console.log(balances.length)
output will be something like this:
0
0
1
2
How to return balances with correct array.length = 2 after map() function?
Try to use this code.
const obj = {
'a': '1234',
'b': '5678'
}
// Sample getBalance function which is returns a promise.
async function getBalance(v) {
return await v;
}
const balances = []
const promises = Object.keys(obj).map(async (key, index) => {
const r = await getBalance(obj[key]);
return Math.floor(Number(r))
})
Promise.all(promises).then(balances => {
console.log(balances)
})
.forEach() or .map() doesn’t respect async/await at all, and Promise.all only works if the order of execution doesn’t matter. The for...of loop executes in the order one would expect — waiting on each previous await operation to complete before moving on to the next.
const obj = {
'a': '1234',
'b': '5678'
}
// Sample getBalance function which is returns a promise.
async function getBalance(v) {
return await v;
}
const balances = []
const getData = async () => {
for(const key of Object.keys(obj)) {
const r = await getBalance(obj[key]);
balances.push(Math.floor(r));
console.log(balances.length);
}
console.log(balances.length);
};
getData();

chain array of promises with bluebird

I'm a working my way with promises and I'm stuck with my use case.
I have an array of transformer functions (each function is a promise and modifies some JSON structure).
Let me show some code.
Lets say this is my JSON structure (array)
var data = [{a: 1, b:2}, {a:3, b:4}];
transformFunction is definition of transform functions modifying the data a certain way. The two functions adds c and d property to the above JSON structure:
var transformFunctions = { //
transform1: function (data) { // This function adds `c` property to each object from `a`
return new Promise(function (resolve) {
for (var i = 0; i < data.length; i++) {
data[i].c = data[i].a;
}
return resolve(data);
})
},
transform2: function (data) { // This function adds `d` property to each object from `c`
return new Promise(function (resolve) {
for (var i = 0; i < data.length; i++) {
data[i].d = data[i].c;
}
return resolve(data);
})
},
...
}
The from a UI user specifies which transformer functions he should use and in what order. Lets say he picked the normal order like this:
var userTransformList = ['transform1', 'transform2'];
The transform1 method should modify the data and the result should be passed to transform2 method.
I was looking at: Promise.all but it seems that it does not care for the order of the promises, and most important it needs to pass the previous result to the next promise.
Note: As adeneo pointed out in the comments, use promises only if you are dealing with asynchronous code.
Create an array of functions which are to be executed. And make sure that they all return a Promise.
Then, you can use Promise.reduce to reduce the initial value to the transformed final value by returning the result of executing current promise returning function, on every iteration.
Finally you can attach a then handler to get the actual value and a catch handler, just in case if the promises are rejected.
Lets say we have two transform functions like this.
Note: I am telling again. You should never use Promises with functions like these. You should use promises only when the functions you are dealing with are really asynchronous.
// Just add a property called `c` to all the objects and return a Promise object
function transform1(data) {
return Promise.resolve(data.map(function(currentObject) {
currentObject.c = currentObject.a + currentObject.b;
return currentObject;
}));
}
// Just add a property called `d` to all the objects and return a Promise object
function transform2(data) {
return Promise.resolve(data.map(function(currentObject) {
currentObject.d = currentObject.a + currentObject.b + currentObject.c;
return currentObject;
}));
}
Then you can transform the original value, like this
Promise.reduce([transform1, transform2], function (result, currentFunction) {
return currentFunction(result);
}, [{a: 1, b: 2}, {a: 3, b: 4}]) // Initial value
.then(function (transformedData) {
console.log(transformedData);
})
.catch(function (err) {
console.error(err);
});
Output
[ { a: 1, b: 2, c: 3, d: 6 }, { a: 3, b: 4, c: 7, d: 14 } ]
You can chain Promises the way you always do: using .then().
Let's say you have these two transformations:
function increment(x) {
return Promise.resolve(x + 1);
}
function double(x) {
return Promise.resolve(2 * x);
}
In a real scenario, these would be doing asynchronous work. You could just:
increment(1).then(double)
But, you don't know the order or number of transformations. Let's put them into an array, and then() them one by one:
var transformations = [increment, double]
var promise = Promise.resolve(1);
for (var i = 0; i < transformations.length; i++)
promise = promise.then(transformations[i]);
You can attach a catch() handler before you start, after you finish or even per-transformation.
This isn't efficient if you're going to apply hundreds of transformations. In that case, you should use reduce() as thefourtheye suggests in his answer.

Categories