using async await with reduce - javascript

i have following
const imageField = ["logoUrl", "fullLogoUrl"]
const onCreate = async (submitData: any) => {
const uploadImageField = await imageField.reduce(
async function (acc: any, cur: string) {
await acc;
const url = await uploadImage(submitData.general[cur][0]);
acc[cur] = url;
return acc;
},
{}
);
console.log(uploadImageField);
}
this is my console.log
{
logoUrl: "https://........"
}
only logoUrl field is show, fullLogoUrl is missing

The problem is that acc on the second iteration is a promise object - that's why you await it. However, you still assign the [cur] property on that promise object, not on the promise result, and the implicit promise chaining of the async function as well as the explicit awaits will just ignore properties on the promise object. You could fix this by doing acc = await acc;, but really I recommend not to use reduce with async/await at all. A normal loop is much simpler and has no pitfalls.
const imageField = ["logoUrl", "fullLogoUrl"]
const onCreate = async (submitData: any) => {
const uploadImageField = {};
for (const cur of imageField) {
const url = await uploadImage(submitData.general[cur][0]);
acc[cur] = url;
}
console.log(uploadImageField);
}

Related

Node JS multliple promises chaining

I have node JS api server and I'm having issues with correct chaining of the Promises:
app.post(
"/api/tasks",
async function (_req, res) {
const newArray = [{ MyTasks: [] }];
const getOne = async (owner, taskID) => {
return await getOneDocument(owner, taskID).then((result) => {
console.log("get one doc", result);
return result;
});
};
// first promise
let toApproveTasks = await getToApproveTasks(_req.body.userID);
console.log("1", toApproveTasks);
// loop trough the result of 1st promise and run async function for each
const arrayToDoc = async (array) => {
array.TasksToApprove.forEach(async (element) => {
let objToPush = await getOne(element.Owner, element.TaskID);
console.log("1.5", objToPush);
newArray.MyTasks.push(objToPush);
});
};
// second promise
await arrayToDoc(toApproveTasks);
console.log("2", newArray);
// third promise
let finalResult = await parseCosmosOutput(newArray);
console.log("3", finalResult);
res.status(200).send(finalResult);
}
);
What I get in console is :
1 [Object] - all good
Emppty Array
Empty Array
get one doc {object} - all good
1.5 {object} - all good
How would I make sure when I loop over result of 1st promise my code awaits async function and pushes to newArray results ?
Use For..of instead of forEach inside arrayToDoc function
E.g
const arrayToDoc = async (array) => {
for(let element of array.TasksToApprove){
let objToPush = await getOne(element.Owner, element.TaskID);
console.log("1.5", objToPush);
newArray.MyTasks.push(objToPush);
}
};

How to use async/await using crypto.randomBytes in nodejs?

const crypto = require('crypto');
async function getKey(byteSize) {
let key = await crypto.randomBytes(byteSize);
return key;
}
async function g() {
let key = await getKey(12);
return key;
}
console.log(g());
console.log('hello - want this called after g() above');
I've been at this for an hour and I can't understand how to ensure that I get a key using async/await. I get a Promise-pending no matter what I do.
I've also tried this:
async function getKey(byteSize) {
let key = await crypto.randomBytes(byteSize);
return key;
}
getKey(12).then((result) => { console.log(result) })
console.log('hello');
... to no avail! Which was inspired by:
How to use await with promisify for crypto.randomBytes?
Can anyone help me with this?
All I'm trying to do is to get randomBytes async. using the async./await block but ensure that it fulfills the promise before I carry on in the code.
This is an extension of my comment on the question
Since you're not promisify'ing or passing a callback to crypto.randomBytes() it is synchronous so you can't await it. Additionally, you're not properly awaiting the promise returned by g() at the top level. That is why you always see the pending Promise in your console.log()
You can use util.promisify() to convert crypto.randomBytes() into a promise returning function and await that. There is no need for the async/await in your example because all that is doing is wrapping a promise with a promise.
const { promisify } = require('util')
const randomBytesAsync = promisify(require('crypto').randomBytes)
function getKey (size) {
return randomBytesAsync(size)
}
// This will print the Buffer returned from crypto.randomBytes()
getKey(16)
.then(key => console.log(key))
If you want to use getKey() within an async/await style function it would be used like so
async function doSomethingWithKey () {
let result
const key = await getKey(16)
// do something with key
return result
}
If the callback function is not provided, the random bytes are generated synchronously and returned as a Buffer
// Synchronous
const {
randomBytes
} = await import('node:crypto');
const buf = randomBytes(256);
console.log(
`${buf.length} bytes of random data: ${buf.toString('hex')}`);
const crypto = require('crypto');
async function getKey(byteSize) {
const buffer = await new Promise((resolve, reject) => {
crypto.randomBytes(byteSize, function(ex, buffer) {
if (ex) {
reject("error generating token");
}
resolve(buffer);
});
}
async function g() {
let key = await getKey(12);
return key;
}
const crypto = require("crypto");
async function getRandomBytes(byteSize) {
return await new Promise((resolve, reject) => {
crypto.randomBytes(byteSize, (err, buffer) => {
if (err) {
reject(-1);
}
resolve(buffer);
});
});
}
async function doSomethingWithRandomBytes(byteSize) {
if (!byteSize) return -1;
const key = await getRandomBytes(byteSize);
//do something with key
}
doSomethingWithRandomBytes(16);

How do I chain get requests and concatinate the result of both into an array JS/Node

Current code is sloppy, I know. I am still iffy on then method.
What each get call returns
an array with some, not all, results
What I need
to pass two different uri and concatenate the result of both and then export it as module
e.g. getinfo(uri).getinfo(uri2).then(concatenated results)
CODE
const request = require('request-promise');
const getInfo = (uri) => {
// Return new promise
return request({
method: 'GET',
uri: uri,
json : true,
headers: {
'User-Agent': 'Request-Promise'
}
});
}
const promise = getInfo(url1).then((val) => {
let store = [];
getInfo(url2).then((val2) => {
store = store.concat(val2.array);
return store;
})
});
//ideally
export promise;
You just need to use .then correctly, and not discard the result of the first request
const request = require('request-promise');
const getInfo = (uri) => {
// Return new promise
return request({
method: 'GET',
uri: uri,
json : true,
headers: {
'User-Agent': 'Request-Promise'
}
});
}
// using promise.then
const result = (url1, url2) => getInfo(url1)
.then(val1 => getInfo(url2)
.then(val2 => val1.array.concat(val2.array))
);
// or async/await
const result = async (url1, url2) => {
const val1 = await getInfo(url1);
const val2 = await getInfo(url2);
return val1.array.concat(val2.array);
};
// both requests done at the same time
const result = (url1, url2) => Promise.all([
getInfo(url1),
getInfo(url2)
]).then(([val1, val2]) => val1.array.concat(val2.array));
export result;
// usage
const fn = require("./module.js"); // whatever you call the above file
fn("url1", "url2").then(result => {
// use result here
});
To explain each incarnation of result - it may be easier to write it out using regular functions so I can add comments
const result = function(url1, url2) {
return getInfo(url1)
.then(function(val1) {
return getInfo(url2)
.then(function(val2) {
return val1.array.concat(val2.array));
})
})
}
Usually you try to avoid "nesting" .then's like this, but since the end result requires both val1 and val2, it's unavoidable (not really, but, lets say it is)
This is where async/await shines
const result = async (url1, url2) => {
const val1 = await getInfo(url1);
const val2 = await getInfo(url2);
return val1.array.concat(val2.array);
};
There's no need to even rewrite that, because it's clear!
However, you want to run in Parallel
const result = (url1, url2) => Promise.all([
getInfo(url1),
getInfo(url2)
]).then(([val1, val2]) => val1.array.concat(val2.array));
Promise.all takes an array of promises, and returns a promise that resolves to the array of resolved results
([val1, val2]) => //rest of code
in case you didn't know is like
(results => {
let val1 = results[0];
let val2 = results[1];
// rest of code
So, that should be fairly easy to understand as it is

Array reduce function with async await

I'm trying to skip one object from an array objects based on a async operator. I've tried following cases, but getting a Type error.
Tried Method 1
newObjectArray = await Promise.all(objectAray.reduce(async (result, el) => {
const asyncResult = await someAsyncTask(el);
if (asyncResult) {
result.push(newSavedFile);
}
return result;
}, []));
Tried Method 2
newObjectArray = await Promise.all(objectAray.reduce(async (prevPromise, el) => {
const collection = await prevPromise;
const asyncResult = await someAsyncTask(el);
if (asyncResult) {
prevPromise.push(newSavedFile);
}
collection.push(newSavedFile);
return collection;
}, Promise.resolve([])));
Error
'TypeError: #<Promise> is not iterable',
' at Function.all (<anonymous>)',
In your first try, result is a promise as all async functions evaluate to a promise when called, so you have to await result before you can push to the array, and then you don't need the Promise.all:
newObjectArray = await objectAray.reduce(async (result, el) => {
const asyncResult = await someAsyncTask(el);
if (asyncResult) {
(await result).push(newSavedFile);
}
return result;
}, []);
But I'd guess that it is way faster to just filter afterwards:
newObjectArray = (await Promise.all(objArray.map(someAsyncTask))).filter(el => el);

Is Await inside a Promise ignored?

I have this simple function below, where you would pass in a String as parameter which would return a Promise that would only resolve to either empty object or filled object, based on a firestore sub-collection query.
The problem is the very last resolve({}) is called even if I've invoked an await on the previous statement, what could possibly be happening?
getSubCollections = (id: string): Promise<any> => {
const subCollectionPromise = new Promise(async (resolve: Function) => {
if (typeof this.subCollectionKeys === 'undefined') {
resolve({});
}
const collections = {};
await this.subCollectionKeys.forEach(async (key: string) => {
const subCollection = await this.getCollection()
.doc(id)
.collection(key)
.get();
const firebaseObj = await subCollection.docs.map((obj: Object) =>
this.getShallowData(obj),
);
const fromFirebase = fromFirebaseObj(firebaseObj);
if (!fromFirebase.empty) {
collections[key] = fromFirebase;
}
resolve(collections);
});
resolve({}); /// WHY IS THIS CALLED EVEN IF THERE IS AWAIT ON TOP???
});
return subCollectionPromise;
};

Categories