I have a problem with async/await and some Promises.
I have this code. It starts here:
let valid = await LoadRouter.load(body);
console.log(valid);//showing me Pending Promises
The function is:
loadGeneratingUnits(data){
let newUGArray = [];
try {
const result = data.map(async (itemGU, index) => {
const checGU = await this.checkDataGu(itemGU.nombre);
if(!checGU){
let newUG = {
generating_unit_name: itemGU.nombre,
description: (!itemGU.descripcion) ? null : itemGU.descripcion,
it_generating_unit_id: (!itemGU.it_unidad_generadora) ? 0 : itemGU.it_unidad_generadora
}
newUGArray.push(newUG);
}
})
return result;
} catch (error) {
throw new Error(error.message)
}
}
This one is where I have the problems
async checkDataGu(guName = null){
if(guName){
return await generatingUnitModel.findOne({
attributes: [
'id',
'generating_unit_name',
],
where: {
generating_unit_name: guName
}
})
}
}
Any comment about the use of async/await on this code?
By making the callback to data.map() async, data.map() is now transforming the data into an array of Promises, because the return value of an async function is always a Promise. await only will wait for a Promise to resolve, not an array of them. You should use Promise.all for that:
const result = Promise.all(data.map(async (itemGU, index) => {
const checGU = await this.checkDataGu(itemGU.nombre);
if(!checGU){
let newUG = {
generating_unit_name: itemGU.nombre,
description: (!itemGU.descripcion) ? null : itemGU.descripcion,
it_generating_unit_id: (!itemGU.it_unidad_generadora) ? 0 : itemGU.it_unidad_generadora
}
newUGArray.push(newUG);
}
}))
Now result is one Promise that will resolve with an array of the values each inner Promise resolved with. Ultimately this means your upper let valid = await LoadRouter.load(body); should resolve with the array you expect.
Related
Been on this for over 24 hours, everything seems to work as I want but the promise keeps returning null.
[
null,
null
]
here are my codes:
let vettedBatch = currentBatch.Items.map((current) => {
getUser(current.userId).then((res) => {
// return resolve(JSON.parse(res.body));
let body = JSON.parse(res.body);
if (body.hasOwnProperty("Item")) {
return body;
} else {
//if user does not exist on the users table based on ID, lets take his transaction out of the fail-safe table
console.log(
`user with id number ${current.userId} with transaction id ${current.txId} do not exist or must have been disabled`
);
User.deleteWithdrawalTx(current.txId).then(() => {
console.log(
`transaction id ${current.txId} delete for unknown user with userId ${current.userId}`
);
});
}
});
});
You need to use Promise.all:
const data = [];
const promises = currentBatch.Items.map(async current => {
return await getUser(current.userId)
});
Promise.all(promises)
.then(res => {
res.map(item => {
let { body } = JSON.parse(item);
if (body.hasOwnProperty("Item")) {
data.push(body);
} else {
console.log('message');
}
})
})
Instead of mixing async/await and Promise syntax, I would suggest you to stick to one.
Here would be your code written fully in async/await syntax:
const getData = async () => { // async function instead of new Promise()...
return Promise.all(currentBatch.Items.map(async (current) => { // make map async and await it with Promise.all()
const res = await getUser(current.userId); // await instead of .then()
let body = JSON.parse(res.body);
if (body.hasOwnProperty("Item")) {
return body;
} else {
console.log(`user with id number ${current.userId} with transaction id ${current.txId} do not exist or must have been disabled`);
await User.deleteWithdrawalTx(current.txId); // await instead of .then()
console.log(`transaction id ${current.txId} delete for unknown user with userId ${current.userId}`);
// You should return something here too, but I dont know what you want to return, so...
}
}));
}
let vettedBatch = await getData(); // await the async function
Your problem is actually a deviation of this question: How do I return the response from an asynchronous call?. Should be fixed in my answer, but I still suggest you to check out the linked thread.
All async/await code can be translated to Promises, or other constructs. Because this is what transpilation with babel has done.
I has assumed the two paradigms where equivalent and that all promises could be rewritten with async/await. Is this true? Or is it an assumption I need to drop.
For a concrete example I have the following code, which contains a promise.
I have not seen a way to translate this code to async/await only.
For context, this Mailbox code is for a demo I have to explain the Actor model in the context of the browser/JavaScript
function Mailbox () {
const messages = []
var awaiting = undefined
this.receive = () => {
if (awaiting) { throw 'Mailbox alread has a receiver'}
return new Promise((resolve) => {
if (next = messages.shift()) {
resolve(next)
} else {
awaiting = resolve
}
})
}
this.deliver = async (message) => {
messages.push(message)
if (awaiting) {
awaiting(messages.shift())
awaiting = undefined
}
}
}
async/await use promises. In fact, they don't do anything useful if you don't have a promise to await. They don't replace promises. You would typically use await instead of .then() on a promise.
Let's look at a simple example. Suppose you have two functions that return promises.
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
const rp = require('request-promise');
function getData(uri) {
let options = {uri, json: true};
return rp(options);
}
Now, you want to get data from three different URLs with a 1 second delay between the requests.
Regular Promises
With regular promises (no async/await), you could do something like this using promise chaining:
function getAllData() {
let result = {};
return getData(firstURL).then(data => {
results.d1 = data;
return delay(1000);
}).then(() => {
return getData(secondURL);
}).then(data => {
results.d2 = data;
return delay(1000);
}).then(() => {
return getData(thirdURL);
}).then(data => {
results.d3 = data;
return results;
});
}
getAllData().then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
Using Async/Await
Using await, you can simplify the sequencing of multiple asynchronous operations, but those asynchronous operations STILL use promises. You are just replacing some of the chained .then() operations with await.
async function getAllData() {
let result = {};
result.d1 = await getData(firstURL);
await delay(1000);
result.d2 = await getData(secondURL);
await delay(1000);
result.d3 = await getData(thirdURL);
await delay(1000);
return result;
}
getAllData().then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
After a bunch of looking into Futures, Promises, wrapAsync, I still have no idea how to fix this issue
I have this method, which takes an array of images, sends it to Google Cloud Vision for logo detection, and then pushes all detected images with logos into an array, where I try to return in my method.
Meteor.methods({
getLogos(images){
var logosArray = [];
images.forEach((image, index) => {
client
.logoDetection(image)
.then(results => {
const logos = results[0].logoAnnotations;
if(logos != ''){
logos.forEach(logo => logosArray.push(logo.description));
}
})
});
return logosArray;
},
});
However, when the method is called from the client:
Meteor.call('getLogos', images, function(error, response) {
console.log(response);
});
the empty array is always returned, and understandably so as the method returned logosArray before Google finished processing all of them and returning the results.
How to handle such a case?
With Meteor methods you can actually simply "return a Promise", and the Server method will internally wait for the Promise to resolve before sending the result to the Client:
Meteor.methods({
getLogos(images) {
return client
.logoDetection(images[0]) // Example with only 1 external async call
.then(results => {
const logos = results[0].logoAnnotations;
if (logos != '') {
return logos.map(logo => logo.description);
}
}); // `then` returns a Promise that resolves with the return value
// of its success callback
}
});
You can also convert the Promise syntax to async / await. By doing so, you no longer explicitly return a Promise (but under the hood it is still the case), but "wait" for the Promise to resolve within your method code.
Meteor.methods({
async getLogos(images) {
const results = await client.logoDetection(images[0]);
const logos = results[0].logoAnnotations;
if (logos != '') {
return logos.map(logo => logo.description);
}
}
});
Once you understand this concept, then you can step into handling several async operations and return the result once all of them have completed. For that, you can simply use Promise.all:
Meteor.methods({
getLogos(images) {
var promises = [];
images.forEach(image => {
// Accumulate the Promises in an array.
promises.push(client.logoDetection(image).then(results => {
const logos = results[0].logoAnnotations;
return (logos != '') ? logos.map(logo => logo.description) : [];
}));
});
return Promise.all(promises)
// If you want to merge all the resulting arrays...
.then(resultPerImage => resultPerImage.reduce((accumulator, imageLogosDescriptions) => {
return accumulator.concat(imageLogosDescriptions);
}, [])); // Initial accumulator value.
}
});
or with async/await:
Meteor.methods({
async getLogos(images) {
var promises = [];
images.forEach(image => {
// DO NOT await here for each individual Promise, or you will chain
// your execution instead of executing them in parallel
promises.push(client.logoDetection(image).then(results => {
const logos = results[0].logoAnnotations;
return (logos != '') ? logos.map(logo => logo.description) : [];
}));
});
// Now we can await for the Promise.all.
const resultPerImage = await Promise.all(promises);
return resultPerImage.reduce((accumulator, imageLogosDescriptions) => {
return accumulator.concat(imageLogosDescriptions);
}, []);
}
});
I have a function with an array of promises, that array can have from 1 to X promises.
Those promises enter into the array based on conditionals.
I want to be able to distinguish from which API comes each result, and I can't realise a clean way to do it
let promises = [];
if (false) {
let promise1 = request(toUrl);
promises.push(promise1);
}
if (true) {
let promise2 = request(toUrl);
promises.push(promise2);
}
if (false) {
let promise3 = request(toUrl);
promises.push(promise3);
}
if (true) {
let promise4 = request(toUrl);
promises.push(promise4);
}
try {
let result = await Promise.all(promises);
} catch (error) {
console.log(error);
}
So, if everything goes ok result will be an array of results. Not knowing which one of the conditionals was true, how do I know if result[0] is the result of promise1, promise2 or promise3?
You can just add to the response of your request(url) another information about the promise like
const promise1 = request(url).then(res => ({ res: res, promise: 'promise1' }))
and at the Promise.all() you will get values of the promises in the above form and can detect which promises were resolved.
Example
const promises = [];
if(true) {
const promise1 = fetch('https://jsonplaceholder.typicode.com/posts/1').then(res => ({ res: res, promise: 'promise1' }));
promises.push(promise1);
}
if(false) {
const promise2 = fetch('https://jsonplaceholder.typicode.com/posts/2').then(res => ({ res: res, promise: 'promise2' }));
promises.push(promise2);
}
Promise.all(promises).then(res => console.log(res));
In my opinion we can simplify the complexity of the problem by using following code -
let promises = [];
let truthyValue = true,
falsyvalue = true;
let [promise1, promise2, promise3, promise4] = await Promise.all([
truthyValue ? request(toUrl) : Promise.resolve({}),
truthyValue ? request(toUrl) : Promise.resolve({}),
falsyValue ? request(toUrl) : Promise.resolve({}),
falsyValue ? request(toUrl) : Promise.resolve({})
]);
// promise1 will be called only when truthyValue is set
if (promise1) {
// do something
}
// promise2 will be called only when truthyValue is set
if (promise2) {
// do something
}
// promise3 will be called only when falsyValue is set
if (promise3) {
// do something
}
// promise4 will be called only when falsyValue is set
if (promise4) {
// do something
}
I used an object map of promises with a name key in order to identify which resolve corresponds to which promise.
const promises = {};
const mapResolveToPromise = res => Object.fromEntries(
Object.entries(promises).map(([key], index) => [key, res[index]])
);
promises.promise1 = fetch('https://jsonplaceholder.typicode.com/posts/1');
promises.promise2 = fetch('https://jsonplaceholder.typicode.com/posts/2');
Promise.all(Object.values(promises))
.then(mapResolveToPromise)
.then(res => {
console.log(res.promise1.url);
console.log(res.promise2.url);
});
I had a similar problem, always a different quantity of async functions to call.
What I didn't want was to start the promises work before promise.all().
So I collected function pointers in an array.
eg:
async function first() {
return new Promise((resolve)=> setTimeout(resolve,1000,99));
}
async function second() {
return new Promise((resolve)=> setTimeout(resolve,1500,100));
}
let x = [first, second];
// x is transformed into an array with then executed functions
await Promise.all(x.map(x=>x()))
Result is:
[
99,
100
]
hopefully this helps and I understood the mentioned problem ... :)
Why all the pushes, you can construct arrays inline.
doPromiseStuff = async ({ thing = true }) => {
const urls = ['', '', '', ''];
return await Promise.all([
thing ? request(urls[1]) : request(urls[2]),
thing ? request(urls[3]) : request(urls[4])
]);
}
Why do my promises stay in the pending state and how do I fix it?
var foundPeopleA = findPeopleA().then(function(result) {
var res = []
result.map(function(el) {
res.push(getProfileXML(el.sid));
});
return res
});
var foundPeopleB = findPeopleB().then(function(result) {
var res = []
result.map(function(el) {
res.push(getProfileXML(el.sid));
});
return res
})
return Promise.all([findPeopleA, findPeopleB]).then(function(results) {
console.log(results) //[ [ Promise { <pending> }, Promise { <pending> } ], [ Promise { <pending> }, Promise { <pending> } ] ]
})
However if i change the body of the 2 functions above into
var res
result.map(function(el) {
res = getProfileXML(el.sid);
});
return res
they won't be pending and I'll get the result.
Arrays are not promises. If you return an array of promises, then gets an array of promises - just like if you return any other non-promise value. Only if you return a promise, the promise will get executed before then. Your foundPeopleA and foundPeopleB each construct an array of promises; you need to concatenate those arrays and pass them to Promise.all or equivalent in order to get them executed.
The problem is that you are handling the promises fulfillment on each of them individually by using then, and all handles the fulfillment of multiple promises by passing it an array of unresolved promises. It builds a new promise with the results of all of them together. Just use:
Promise.all([findPeopleA(), findPeopleB()])
.then(function(responses) ...
Try assigning your Array to the result of the mapping.
var foundPeopleA = findPeopleA().then(function(result) {
var res = []
res = result.map(function(el) {
return getProfileXML(el.sid);
});
return res
});
Or, perhaps you can resolve the promise?
var foundPeopleA = findPeopleA().then(function(result) {
var res = []
res = result.map(function(el) {
return getProfileXML(el.sid);
});
resolve(res);
});
Either way, I belive you need to build your array by returning values from the mapping to create the new array.