Javascript async/await response not ready - node.js

I am having difficulties getting and moving a response from the following functions in a chain.
The values exist further up but however I try to pass them down, the output always ends up empty.
I'm new to async/await in nodejs, but assumed they didn't need to be resolved like or with a promise.
let output = {
keywords: [],
steps: [],
twitter: []
}
async function requestGoogle (output) {
const GoogleResponse = await (
new Promise((resolve, reject) => {
const loc = utilities.checkOrigin()
googleMaps.directions({
origin: loc.origin,
destination: loc.destination
}, (error, response) => {
if (error) resolve(error)
resolve({
response
})
})
})
)
return GoogleResponse
}
async function requestTwitter (output) {
try {
const loc = utilities.checkOrigin()
const twitterParams = {
count: details.twitter.count,
geocode: loc.lat + loc.long + details.radius,
lang: details.lang,
result_type: 'recent'
}
await output.keywords.map(keyword => {
TwitterClient.get('search/tweets', {q: 'query'}, (error, tweets, response) => {
if (error) console.log(error)
tweets.statuses.map(tweet => {
output['twitter'].push({
text: tweet.text
})
})
})
})
return output
} catch (error) {
console.error('++ requestTwitter flow', error)
}
}
flow.commuteCheck = () => {
return requestGoogle(output)
.then(requestTwitter(output))
.then(console.log(output))
}
The response is unmodified and empty:
{ keywords: [], steps: [], twitter: [] }
full file on Github

.then(requestTwitter(output)) calls requestTwitter(output) and passes its return value (a promise) into then. But then expects a callback, not a promise. Similarly with .then(console.log(output)).
You probably meant to wrap those in functions:
flow.commuteCheck = () => {
return requestGoogle(output)
.then(() => requestTwitter(output))
// ---^^^^^^
.then(() => console.log(output))
// ---^^^^^^
}
FWIW, if requestGoogle and requestTwitter don't have to run in series, you can overlap them so the requests run concurrently:
flow.commuteCheck = () => {
return Promise.all([
requestGoogle(output),
requestTwitter(output)
]).then(() => console.log(output));
}
It's also probably worth noting that in both cases, the promise resolves with no useful value for the caller. You might have it resolve with output. To provent crosstalk between requests, you could also make output not be a module global that gets reused, but instead an object you create for each request:
flow.commuteCheck = () => {
const output = output = {
keywords: [],
steps: [],
twitter: []
};
return Promise.all([
requestGoogle(output),
requestTwitter(output)
]).then(() => {
console.log(output);
return output;
});
}

Related

how can I get, fetched data from neo4j?

I was trying to fetch data from neo4j database.
Here is my function for getting data from database which I found on their official website and have modified it little bit:
function receiveDataFromDB() {
var neo4j = require("neo4j-driver");
var driver = neo4j.driver(
"neo4j://localhost",
neo4j.auth.basic("neo4j", "something")
);
console.log(driver);
var session = driver.session({
database: "neo4j",
defaultAccessMode: neo4j.session.READ,
});
session.run(`match (n) return n`).subscribe({
onKeys: (keys) => {
console.log(keys);
},
onNext: (record) => {
console.log(record.get("n"));
},
onCompleted: () => {
session.close(); // returns a Promise
},
onError: (error) => {
console.log(error);
},
});
}
So this function only console.log-s it:
but I want it to use outside the function. I've tried returning return record.get("n") inside onNext but got errors instead.
You can simply use the try-catch equivalent of your query, like this:
try {
const result = session.run(`match (n) return n`);
} catch (error) {}
finally {
session.close();
}
Or try setting your result in a variable, like this:
const result = [];
session.run(`match (n) return n`).subscribe({
onKeys: (keys) => {
console.log(keys);
},
onNext: (record) => {
result.push(record.get("n"));
},
onCompleted: () => {
session.close(); // returns a Promise
},
onError: (error) => {
console.log(error);
},
});

Await doesn't actually wait for an async function to complete

I'm starting to use Redis for caching in my NodeJS app. Unlike all the tutorials that can be found on the internet (for example this one: https://medium.com/geekculture/fasten-your-node-js-application-with-a-powerful-caching-mechanism-using-redis-fd76b8aa482f), I don't send the res just after calling the "cache" function. My "cache" function is a middleware returning a value if found.
Here is where the function is called:
const { requestLDAP } = require('../utils/ldap')
router.get('/:mode/:type/:code', async (req, res) => {
(...)
const resultLDAP = await requestLDAP(
type, params
);
console.log('---');
console.log(resultLDAP);
console.log('---');
And here is the part of the LDAP module:
async function requestLDAP(type, params) {
const key = JSON.stringify({type,params});
// checking the cache
await clientRedis.get(key, async (err, response) => {
if (response) {
console.log("[CACHE]");
return {
ok: true,
contenu: JSON.parse(response),
};
} else {
const simulated = Array.from({ length: 5 }, () => {
let r = { type: type.slice(0, -1) };
params.forEach((k) => {
r[k] = `${cle}_${Math.floor(100*Math.random(100))}`;
});
return r;
});
console.log("[API]");
await clientRedis.setex(cle, 10, JSON.stringify(simulated));
console.log('[SAVED]');
return {
ok: true,
contenu: simulated,
};
(...)
I don't write all the rest, which is basically all the actual LDAP call.
My problem is that the script doesn't seem to wait for the "await" to complete. Here is what I have in the console:
---
undefined
---
---
undefined
---
[CACHE]
[CACHE]
What am I missing here?
If I write console.log(response) I have the correct response, but once again AFTER expected.
[EDIT]
After helpful comments, here is how I wrote the function:
let finalResponse = await clientRedis.get(cle, async (err, response) => {
if (response) {
console.log("[CACHE]");
return response;
} else {
(...)
return {
ok: true,
contenu: finalResponse,
};
but finalResponse is a boolean, it doesn't return the real response.
As they said before, requestLDAP function returns nothing, not an actual Promise that you can wait for.
You need that requestLDAP returns a Promise because you are waiting for the result of clientRedis that you don't know when will you have it.
When you have it, you have to resolve or reject the Promise mentioned. A return here is not a return for requestLDAP, but for the caller of the callback.
Try packing and returning a promise like this:
async function requestLDAP(type, params) {
return new Promise( async (resolve,reject) => { // --> Promisify the asynchronous response
const key = JSON.stringify({type,params});
// checking the cache
await clientRedis.get(key, async (err, response) => {
if(err) reject('Something ugly happened here'); // --> Give a result for the promise in the future
if (response) {
console.log("[CACHE]");
resolve( { // --> Give a result for the promise in the future
ok: true,
contenu: JSON.parse(response),
} );
} else {
const simulated = Array.from({ length: 5 }, () => {
let r = { type: type.slice(0, -1) };
params.forEach((k) => {
r[k] = `${cle}_${Math.floor(100*Math.random(100))}`;
});
return r;
});
console.log("[API]");
await clientRedis.setex(cle, 10, JSON.stringify(simulated));
console.log('[SAVED]');
resolve( { // --> Give a result for the promise in the future
ok: true,
contenu: simulated,
} );
(...)
}
});
}
};

Store data in an array using for each in node js sequelize

I am trying to push the fetched data in an array using foreach but it only returns the first data in the loop. Here is my code.
exports.getAllTrial = async function (req, res, next) {
try {
new Promise( async (resolve, reject) => {
var reservations = [];
await Schedule.getSchedule()
.then(data => {
data.forEach(async (element) => {
await saveReserve.getAllTrial({where: {scheduleID: element.id, date: "8/18/2020"}})
.then(trial => {
trial.forEach(response => {
reservations.push(response.scheduleID)
})
})
console.log(reservations);
resolve(reservations);
})
});
})
.then(value=>{
res.status(200).json(value);
})
.catch(err => {
console.log(err);
});
} catch (e) {
return res.status(400).json({ status: 400, message: e.message });
}
}
My expected output should be: [ 9, 10, 10 ] But it only returns [9].
Async code in a foreach loop is a bad idea, as it won't be executed one after the other. I suggest reading a bit more async/await and the concept of promise, as you are mixing things here (such as mixing await and .then). Also worth looking into Promise.all which will resolve a list of promises and array.map.
While I have no idea of what some variables such as saveReserve are supposed to be or do, your code might be simplified into:
exports.getAllTrial = async (req, res, next) => {
try {
const data = await Schedule.getSchedule()
const reservations = await Promise.all(
data.map(element => {
return saveReserve.getAllTrial({ where: { scheduleID: element.id, date: '8/18/2020' } })
})
)
return res.status(200).json(reservations)
} catch (e) {
return res.status(400).json({ status: 400, message: e.message })
}
}

How to return data after resolving multiple promises simultaneously?

Here I've created a data Object
const data = new Object();
Then I'm calling multiple API's parallelly
datamuse.request(`words?ml=${text}`)
.then((list) => {
data.ml = list;
})
.catch((error) => {
console.log(error);
});
datamuse.request(`words?sp=${text}`)
.then((list) => {
data.sp = list;
})
.catch((error) => {
console.log(error);
});
datamuse.request(`words?rel_trg=${text}`)
.then((list) => {
data.rel = list;
})
.catch((error) => {
console.log(error);
});
datamuse.request(`sug?s=${text}`)
.then((list) => {
data.sug = list;
})
.catch((error) => {
console.log(error);
});
datamuse.request(`words?sl=${text}`)
.then((list) => {
data.sl = list;
})
.catch((error) => {
console.log(error);
});
And finally returning the data:
return data;
And the data is returned as undefined.
I know I'm performing the asynchronous operations simultaneously.
But I don't want to use function generator in this case because it's too slow.
Can anyone help me out to get those values in the data and then return it?
Something like
Promise.all([
datamuse.request(`words?ml=${text}`),
datamuse.request(`words?sp=${text}`),
datamuse.request(`words?rel_trg=${text}`),
datamuse.request(`sug?s=${text}`),
datamuse.request(`words?sl=${text}`),
]).then(([
ml,
sp,
rel,
s,
sl
]) => {
const data = {
ml,
sp,
rel,
s,
sl,
};
}).catch((err) => {
// Deal with error
});
Or even better you gotta add something for sug and words difference, I let you figure it out :)
const elems = [
'ml',
'sp',
'rel_trg',
's',
'sl',
];
Promise.all(elems.map(x => datamuse.request(`words?${x}=${text}`))
.then((rets) => {
const data = elems.reduce((tmp, x, xi) => ({
...tmp,
[x]: rets[xi];
}), {});
}).catch((err) => {
// Deal with error
});
Ok here is one possible soluce for your words and sug problem
const elems = [{
acronym: 'ml',
req: 'words',
}, {
acronym: 'sp',
req: 'words',
}, {
acronym: 'rel_trg',
req: 'words',
}, {
acronym: 's',
req: 'sug',
}, {
acronym: 'sl',
req: 'words',
}];
Promise.all(elems.map(({
acronym,
req,
}) => datamuse.request(`${req}?${acronym}=${text}`))
.then((rets) => {
const data = elems.reduce((tmp, {
acronym,
}, xi) => ({
...tmp,
[acronym]: rets[xi];
}), {});
}).catch((err) => {
// Deal with error
});
Promise.resolve is the way to go here.
In addition, I would suggest separating the logic from the data.
function getResult(where) {
const result = {}
return Promise.all(
Object
.entries(where)
.map(([key, path]) => datamuse.request(path).then(list => result[key] = list))
)
.then(() => result)
}
function createMapForText(text) {
return {
ml: `words?ml=${text}`,
sp: `words?sp=${text}`,
rel: `words?rel_trg=${text}`,
sug: `sug?s=${text}`,
sl: `words?sl=${text}`
}
}
// ----- Testing -----
// Fake `datamuse` for testing purposes only
const datamuse = {
request: (path) => Promise.resolve('hello')
}
getResult(
createMapForText('hello')
).then(console.log.bind(console))

Dialogflow v2 Nodejs Client Library UpdateIntent when adding Training Phrases

I am trying to use the updateIntent function that is part of the Dialogflow v2 Client library for Node.js . The reason I am trying to use it, is to be able to add training phrases to an intent.
I cannot seem to get passed this one. Here is the code I am using for it!:
My GetIntent Function:
async function getIntent(intentId) {
try {
let responses = await intentsClient.getIntent({name: intentId, intentView: 'INTENT_VIEW_FULL'})
const response = responses[0]
// console.log(response)
return new Promise((resolve, reject) => {
resolve(response)
})
} catch (err) {
return new Promise((resolve, reject) => {
reject(err)
})
}
}
My UpdateIntent Function:
async function updateIntent(intent) {
const request = {
intent: intent,
languageCode: 'en-US',
updateMask: {
paths: ['trainingPhrases']
},
intentView: 'INTENT_VIEW_FULL'
}
try {
let responses = await intentsClient.updateIntent(request)
return new Promise((resolve, reject) => {
resolve(response)
})
} catch (err) {
console.log(err)
return new Promise((resolve, reject) => {
reject(err)
})
}
}
The Function that Calls it:
async function testUpdateTraining () {
try {
let intent = await getIntent('projects/small-talk-1-406ae/agent/intents/ac7f0b68-de5c-4b6f-9393-358dd2b0c1bd')
let trainingPhrase = { parts: [{ text: 'How should I behave on the trails?'}],
type: 'EXAMPLE'}
intent.trainingPhrases.push(trainingPhrase)
try {
let updatedIntent = await updateIntent(intent)
} catch (e) {
console.log(e)
console.log('failed to update the intent')
}
} catch (err) {
console.log('failed to get intent')
}
}
Now the weird thing is - I am getting a 200 response from the client library call. The Api doc states that upon a successful response you will get an intent object. I am getting an intent object with the training phrases inside...
[![{ inputContextNames: \[\],
events: \[\],
trainingPhrases:
\[ { parts: \[Array\],
name: 'ad0d1f6a-78cf-4e0b-84ca-ec62a45c75dc',
type: 'EXAMPLE',
timesAddedCount: 0 },
{ parts: \[Array\],
name: 'e33cce4b-96ee-4e35-a151-5b09ff603817',
type: 'EXAMPLE',
timesAddedCount: 0 },
{ parts: \[Array\],
name: '7d9b7c56-5fa8-4791-986f-e57b9f90d431',
type: 'EXAMPLE',
timesAddedCount: 0 } \],
outputContexts: \[\],
parameters: \[\],
messages:
\[ { platform: 'PLATFORM_UNSPECIFIED',
text: \[Object\],
message: 'text' } \],
defaultResponsePlatforms: \[\],
followupIntentInfo: \[\],
name: 'projects/small-talk-1-406ae/agent/intents/ac7f0b68-de5c-4b6f-9393-358dd2b0c1bd',
displayName: 'faq.offroad.card1answer',
priority: 500000,
isFallback: false,
webhookState: 'WEBHOOK_STATE_UNSPECIFIED',
action: 'faq.offroad.card1answer',
resetContexts: false,
rootFollowupIntentName: '',
parentFollowupIntentName: '',
mlDisabled: true }][1]][1]
This is what dialogflow has. Only two training phrases here, the one I added programmatically does not show up.
So my question is, how can I format the request so I can update the training phrases without a problem? Is there an example I can run off?
After trying out a lot, understood that my code worked because i removed update mask. And the languageCode as well, because it was giving me an error.
The code is as below and works fine.
Check it up.
This is the getIntent function:
async function getIntent(intentId) {
try {
let responses = await intentsClient.getIntent({
name: intentId,
intentView: 'INTENT_VIEW_FULL'
})
const response = responses[0];
console.log(util.inspect(response, false, null, true /* enable colors */ ));
return new Promise((resolve, reject) => {
resolve(response)
})
} catch (err) {
return new Promise((resolve, reject) => {
reject(err)
})
}
}
The function that calls it:
async function testUpdateTraining () {
try {
let intent = await getIntent('<your_ID>')
let trainingPhrase = {
parts: [{
text: 'let me call you?'
}],
type: 'EXAMPLE'
}
intent.trainingPhrases.push(trainingPhrase)
try {
let updatedIntent = await updateIntent(intent)
} catch (e) {
console.log(e)
console.log('failed to update the intent')
}
} catch (err) {
console.log('failed to get intent')
}
}
The UpdateIntent function:
async function updateIntent(intent) {
const request = {
intent: intent,
intentView: 'INTENT_VIEW_FULL'
}
try {
let responses = await intentsClient.updateIntent(request)
return new Promise((resolve, reject) => {
resolve(responses)
})
} catch (err) {
console.log(err)
return new Promise((resolve, reject) => {
reject(err)
})
}
}
You can try:
updateMask: {
paths: ['training_phrases']
}
since paths naming convetion is "snake"

Resources