Call async function in map - javascript

I have next code:
contents.map((content) => {
const element = Object.assign({}, content);
if (element.type === 'blabla') {
element.data.id = await getId();
}
return element;
});
But it fails with next error:
The 'await' operator can only be used in an 'async' function

Make each map function async and use Promise.all():
await Promise.all(contents.map(async content => ...);
The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises. It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Note that this means all content items are processed in parallel. If you want sequential execution, use for ... of instead. Each iteration may await as needed.

Just add async in starting of your function.
You cant use await without async.
contents.map(async (content) => {
const element = Object.assign({}, content);
if (element.type === 'blabla') {
element.data.id = await getId();
}
return element;
});
You have to provide whole code for correct result, i just wanted to tell you the mistake you are doing.

Related

Javascript - Returning a variable and a promise from a map function [duplicate]

I do realise that when returning a non-promise in a .then() handler, it is immediately passed on to the next handler, if it's a promise that is returned, executing is halted for the promise to resolve before it is passed on to the next handler.
Also I know that only one value can be returned from a promise.
That beeing said, how would I go about returning multiple parameters from one .then() handler to the next one? Esepcailly if it's a mix of promises and non-promises. Currently I put everything into a custom object, return it, and use async await in the following then() handler for the promises to reslolve.
Then is use the resolved promise values and the non-promise value to do some work together.
This works fine but my gut is saying that this is somehow not the way it is supposed to be... maybe?
Example:
const current = 'blah';
const previous = 'blubb';
this.doSomeAsyncWork()
.then(
result => {
const nonPromiseValue = new domSomethingSynchronous(current, previous);
// "custom object, mix of promises and non-promises"
return {
nonPromise: nonPromise,
promiseA: ControllerA.asyncOperationA(current, nonPromiseValue.someProperty),
promiseB: ControllerB.asyncOperationB(nonPromiseValue.someOtherProperty),
}
}
)
.then(
async x => {
const nonPromiseValue = x.nonPromiseValue;
const valueA = await x.promiseA;
const valueB = await x.promiseB;
// do something with the results of those three variables
}
)
.catch(
// ...
)
Use return Promise.all on the array of Promises and non-Promises at the end of a .then, and then you can destructure the results immediately in the next .then, no await nor async needed.
The Promise.all will resolve once all Promises in the array have resolved. The non-Promises passed to it will just be passed to the next .then.
const makeProm = () => new Promise(resolve => setTimeout(resolve, 1000, 'resolveValue'));
Promise.resolve()
.then(() => {
const prom = makeProm();
const otherValue = 'foo';
return Promise.all([prom, otherValue]);
})
.then(([resolveValue, otherValue]) => {
console.log(resolveValue, otherValue);
});

Access response object from json promise with fetch() in javascript [duplicate]

I do realise that when returning a non-promise in a .then() handler, it is immediately passed on to the next handler, if it's a promise that is returned, executing is halted for the promise to resolve before it is passed on to the next handler.
Also I know that only one value can be returned from a promise.
That beeing said, how would I go about returning multiple parameters from one .then() handler to the next one? Esepcailly if it's a mix of promises and non-promises. Currently I put everything into a custom object, return it, and use async await in the following then() handler for the promises to reslolve.
Then is use the resolved promise values and the non-promise value to do some work together.
This works fine but my gut is saying that this is somehow not the way it is supposed to be... maybe?
Example:
const current = 'blah';
const previous = 'blubb';
this.doSomeAsyncWork()
.then(
result => {
const nonPromiseValue = new domSomethingSynchronous(current, previous);
// "custom object, mix of promises and non-promises"
return {
nonPromise: nonPromise,
promiseA: ControllerA.asyncOperationA(current, nonPromiseValue.someProperty),
promiseB: ControllerB.asyncOperationB(nonPromiseValue.someOtherProperty),
}
}
)
.then(
async x => {
const nonPromiseValue = x.nonPromiseValue;
const valueA = await x.promiseA;
const valueB = await x.promiseB;
// do something with the results of those three variables
}
)
.catch(
// ...
)
Use return Promise.all on the array of Promises and non-Promises at the end of a .then, and then you can destructure the results immediately in the next .then, no await nor async needed.
The Promise.all will resolve once all Promises in the array have resolved. The non-Promises passed to it will just be passed to the next .then.
const makeProm = () => new Promise(resolve => setTimeout(resolve, 1000, 'resolveValue'));
Promise.resolve()
.then(() => {
const prom = makeProm();
const otherValue = 'foo';
return Promise.all([prom, otherValue]);
})
.then(([resolveValue, otherValue]) => {
console.log(resolveValue, otherValue);
});

Why is `Promise.then()` being executed before finishing `Promise.all()`?

I want to execute code after a list of files is created but that code is being executed before.
Check the comments in the following psudo code for better understanding.
Pseudo Code:
var fs = require('fs');
Promise.all([
fs.writeFile(...),
list.forEach(element => { // I think the problem is here
fs.writeFile(...); // The files content is big
console.log(element.name); // This is printing after then() method
})
]).then((v) => {
console.log(v); // This is printing before the files being created
});
I hope to have explained my problem well. Thanks in advance.
Promise.all expect an array of Promise. If what you pass in the array is not a Promise, it will be resolved immediately.
fs.writeFile does not return a Promise, it is a nodeJS function that expect a callback of the form (err, result). You can use util.promisify to make it a function returning a Promise.
Your second argument is even less a Promise, as it is just a forEach. It can be addressed by returning instead another Promise.all of list.map(...) where the mapping function return a promise.
The Promise returned by Promise.all() will be resolved if all promises that you passed to it are resolved.
Note that fs.writeFile does not return promises, but you could use the fs Promises API by importing
var fs = require('fs').promises
instead (but beware it's experimental).
But then you would still only pass one promise: fs.writeFile(...).
list.forEach(...) does not return a Promise so Promise.all() can't wait for it.
Try something like (pseudo code):
Promise.all([
fs.writeFile(...),
...list.map(element => { // convert list to an array of promises
console.log(element.name);
return fs.writeFile(...);
})
]).then((v) => {
console.log(v);
});
Using bluebird promisify, we can get a promise return.
const writeFile = require("bluebird").promisify(fs.writeFile);
Promise.all([
fs.writeFile(...),
...list.map(element => {
console.log(element.name);
return writeFile(...);
})
]).then((v) => {
console.log(v);
});
Just for reference I am demonstrating a way to make any asynchronous function to a promise.
const fs = require('fs');
const writeFile = function(file, data, options){
new Promise((resolve,reject) => {
fs.writeFile(file, data, options, (err) => {
if(err){
return reject(err);
}
return resolve();
});
});
}
Now you can use this writeFile function as following
writeFile(filePath, data, options)
.then(() => {
// operation successful, handle your logic here
}).catch((e) => {
// something is wrong, handle the error here.
});
Once you know how the promise works, then its really simple to convert async functions.
To use this function in Promise.all we can do the following-
Promise.all([
writeFile(...), // pass your arguments here
...list.map(element => { // convert list to an array of promises
// use the function to create a promise as following,
// make sure to match the arguments as per your need
return writeFile(element.filePath, element.data, element.options);
})
]).then((v) => {
console.log(v);
});
To summarize what is going on here, we are creating an array of promises and passing it to the Promise.all function. And we are creating the array of promises using a base function writeFile which returns a promise and resolves after the file is written. We are using es6 spread operator ... to spread the elements of list object.
I hope this might help someone in future.

What's the best way to return multiple values in a promise chain

I do realise that when returning a non-promise in a .then() handler, it is immediately passed on to the next handler, if it's a promise that is returned, executing is halted for the promise to resolve before it is passed on to the next handler.
Also I know that only one value can be returned from a promise.
That beeing said, how would I go about returning multiple parameters from one .then() handler to the next one? Esepcailly if it's a mix of promises and non-promises. Currently I put everything into a custom object, return it, and use async await in the following then() handler for the promises to reslolve.
Then is use the resolved promise values and the non-promise value to do some work together.
This works fine but my gut is saying that this is somehow not the way it is supposed to be... maybe?
Example:
const current = 'blah';
const previous = 'blubb';
this.doSomeAsyncWork()
.then(
result => {
const nonPromiseValue = new domSomethingSynchronous(current, previous);
// "custom object, mix of promises and non-promises"
return {
nonPromise: nonPromise,
promiseA: ControllerA.asyncOperationA(current, nonPromiseValue.someProperty),
promiseB: ControllerB.asyncOperationB(nonPromiseValue.someOtherProperty),
}
}
)
.then(
async x => {
const nonPromiseValue = x.nonPromiseValue;
const valueA = await x.promiseA;
const valueB = await x.promiseB;
// do something with the results of those three variables
}
)
.catch(
// ...
)
Use return Promise.all on the array of Promises and non-Promises at the end of a .then, and then you can destructure the results immediately in the next .then, no await nor async needed.
The Promise.all will resolve once all Promises in the array have resolved. The non-Promises passed to it will just be passed to the next .then.
const makeProm = () => new Promise(resolve => setTimeout(resolve, 1000, 'resolveValue'));
Promise.resolve()
.then(() => {
const prom = makeProm();
const otherValue = 'foo';
return Promise.all([prom, otherValue]);
})
.then(([resolveValue, otherValue]) => {
console.log(resolveValue, otherValue);
});

async/await inside arrow functions (Array#map/filter)

I'm getting compile time error in this code:
const someFunction = async (myArray) => {
return myArray.map(myValue => {
return {
id: "my_id",
myValue: await service.getByValue(myValue);
}
});
};
Error message is:
await is a reserved word
Why can't I use it like this?
You can't do this as you imagine, because you can't use await if it is not directly inside an async function.
The sensible thing to do here would be to make the function passed to map asynchronous. This means that map would return an array of promises. We can then use Promise.all to get the result when all the promises return. As Promise.all itself returns a promise, the outer function does not need to be async.
const someFunction = (myArray) => {
const promises = myArray.map(async (myValue) => {
return {
id: "my_id",
myValue: await service.getByValue(myValue)
}
});
return Promise.all(promises);
}
If you want to run map with an asynchronous mapping function you can use the following code:
const resultArray = await Promise.all(inputArray.map(async (i) => someAsyncFunction(i)));
How it works:
inputArray.map(async ...) returns an array of promises - one for each value in inputArray.
Putting Promise.all() around the array of promises converts it into a single promise.
The single promise from Promise.all() returns an array of values - the individual promises each resolve to one value.
We put await in front of Promise.all() so that we wait for the combined promise to resolve and store the array of resolved sub-promises into the variable resultArray.
In the end we get one output value in resultArray for each item in inputArray, mapped through the function someAsyncFunction. We have to wait for all async functions to resolve before the result is available.
That's because the function in map isn't async, so you can't have await in it's return statement. It compiles with this modification:
const someFunction = async (myArray) => {
return myArray.map(async (myValue) => { // <-- note the `async` on this line
return {
id: "my_id",
myValue: await service.getByValue(myValue)
}
});
};
Try it out in Babel REPL
So… it's not possible to give recommendation without seeing the rest of your app, but depending on what are you trying to do, either make the inner function asynchronous or try to come up with some different architecture for this block.
Update: we might get top-level await one day: https://github.com/MylesBorins/proposal-top-level-await
it will be 2 instructions, but just shift "await" with the extra instruction
let results = array.map((e) => fetch('....'))
results = await Promise.all(results)
I tried all these answers but no one works for my case because all answers return a promise object not the result of the promise like this:
{
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(3)
0: ...an object data here...
1: ...an object data here...
2: ...an object data here...
length: 3
[[Prototype]]: Array(0)
}
Then I found this answer https://stackoverflow.com/a/64978715/8339172 that states if map function is not async or promise aware.
So instead of using await inside map function, I use for loop and await the individual item because he said that for loop is async aware and will pause the loop.
When you want each remapped value resolved before moving on to the next, you can process the array as an asynchronous iterable.
Below, we use library iter-ops, to remap each value into promise, and then produce an object with resolved value, because map itself shouldn't be handling any promises internally.
import {pipe, map, wait, toAsync} from 'iter-ops';
const i = pipe(
toAsync(myArray), // make asynchronous
map(myValue => {
return service.getByValue(myValue).then(a => ({id: 'my_id', myValue: a}))
}),
wait() // wait for each promise
);
(async function() {
for await (const a of i) {
console.log(a); // print resulting objects
}
})
After each value, we use wait to resolve each remapped value as it is generated, to keep resolution requirement consistent with the original question.

Categories