I have 3 functions and each of them return a promise. How can I assign the response from each promise to a defined object?
This is my code:
const runResponse = {
webAudits: [],
webJourneys: [],
appJourneys: []
};
webAuditsFailures(req)
.then(
appJourneysFailures(req)
)
.then(
webJourneysFailures(req)
).then(
res.status(201).json({ reports: runResponse })
);
This is what I've tried:
webAuditsFailures(req)
.then(
(response) => {
runResponse.webAudits = response
},
appJourneysFailures(req)
)
.then(
(response) => {
runResponse.appJourneys = response
},
webJourneysFailures(req)
).then(
(response) => {
runResponse.webJourneys = response
},
res.status(201).json({ reports: runResponse })
);
But it doesn't works as expected because the webAuditsFailures is called again even if it didn't end and I don't understand why...
These are other failed attempts to fix this:
Using await
const webAudits = await webAuditsFailures(req);
const appJourneys = await appJourneysFailures(req);
const webJourneys = await webJourneysFailures(req);
runResponse.webAudits = webAudits;
runResponse.webJourneys = webJourneys;
runResponse.appJourneys = appJourneys;
The same thing happens with this:
const webAudits = await webAuditsFailures(req);
runResponse.webAudits = webAudits;
Using the co module:
co(function* () {
var runResponse = yield {
webAudits: webAuditsFailures(req),
webJourneys: appJourneysFailures(req),
appJourneys: webJourneysFailures(req)
};
res.status(201).json({ reports: runResponse });
});
Using Promise.all:
Promise.all([webAuditsFailures(req), appJourneysFailures(req),
webJourneysFailures(req)])
.then(function(allData) {
res.status(201).json({ reports: allData });
});
This is the webAuditsFailures function, which sequentially calls another functions that return a promise
export default async (req) => {
const report = req.body.webAudits;
const def = deferred();
if(report.length > 0) {
var reportList = [];
for(const [reportIndex, item] of report.entries()) {
for(const [runIndex, run] of item.runs.entries()) {
const result = await waComplianceBusiness(req, run.id);
var failureList = [];
if(result.data.overviews) {
const compliance = result.data.overviews[0].compliance;
if(compliance) {
for(const [index, rule] of compliance.entries()) {
const response = await waRuleOverview(req, run.id, rule.id);
const failedConditions = response.data.failedConditions;
const ruleName = response.data.ruleName;
if(response.data.pagesFailed > 0) {
for(const [condIndex, condition] of failedConditions.entries()) {
const request = {
itemId: condition.conditionResult.id,
itemType: condition.conditionResult.idType,
parentId: condition.conditionResult.parentId,
parentType: condition.conditionResult.parentType
}
const body = {
runId: run.id,
ruleId: rule.id,
payload: request
}
waConditionOverview(req, body).done(response => {
// do stuff here
});
}
}
}
if(failureList.length > 0) {
item.runs[runIndex].failures = failureList;
}
}
}
}
}
def.resolve(report);
return def.promise
}
else {
return [];
}
}
This is the problem:
waConditionOverview(req, body).done(response => {
// do stuff here
});
You're performing an async action but not waiting for the result. Don't use the deferred model - use util.promisify for callbacks.
In addition, I warmly recommend not mutating the request/resposne like this but storing the information in objects and returning those.
Here is how you'd write the code:
export default async (req) => {
const report = req.body.webAudits;
if(report.length === 0) return;
const runs = Array.from(report.entries(), ([i, item]) => item.runs.entries());
for (const [_, run] of runs) {
const result = await waComplianceBusiness(req, run.id);
var failureList = [];
if (!result.data.overviews) {
continue;
}
const compliance = result.data.overviews[0].compliance;
if(!compliance) {
continue;
}
for(const [_, rule] of compliance.entries()) {
const response = await waRuleOverview(req, run.id, rule.id);
const { failedConditions, ruleName} = response.data;
if(failureList.length > 0) {
item.runs[runIndex].failures = failureList;
}
if(response.data.pagesFailed === 0) continue;
for(const [_, condition] of failedConditions.entries()) {
const request = {
itemId: condition.conditionResult.id,
itemType: condition.conditionResult.idType,
parentId: condition.conditionResult.parentId,
parentType: condition.conditionResult.parentType
}
const body = { runId: run.id, ruleId: rule.id, payload: request}
const reponse = await waConditionOverview(req, body);
// do stuff here
// update response
// update report, so next time we try it's updated and doesn't return empty;
}
}
}
return report;
}
In a promise chain, the current .then() should return a promise. The result of this promise will be passed to the next .then():
webAuditsFailures(req)
.then((response) => {
runResponse.webAudits = response;
return appJourneysFailures(req); // return a promise
})
.then((response) => { // response contains the result of the promise
runResponse.appJourneys = response;
return webJourneysFailures(req);
})
.then((response) => {
runResponse.webJourneys = response;
res.status(201).json({ reports: runResponse });
});
Depending on what .json() in the last .then() does, you should return that as well if there are other .then()s in the promise chain.
Related
I am using the google translate api to translate data from a json file to french locale and then write it back to a file. I am using a recursive function to iterate over the json file since it is deeply nested. However the execution is not waiting till the translation is completed before it writes to the file. I have tried using callback and promise approaches but i couldn't get it right.
Just for it to work as I required an output as an emergency I have set a timout before the write method is called. It work but I would like to learn the appropriate/correct approach to implement this.
const fs = require('fs')
const {Translate} = require('#google-cloud/translate').v2
require('dotenv').config()
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
const credentials = JSON.parse(process.env.credentials)
const translate = new Translate({
credentials,
projectId: credentials.project_id,
})
let data = {}
// writeJSONTofile should be executed only after readJSONFile execution is completed
//read file
const readJSONFile = () => {
try {
data = JSON.parse(fs.readFileSync('...\\locale\\en.json'))
iterateAndTranslate(data)
setTimeout(() => {
writeJSONToFile()
}, 25000)
} catch (error) {
console.log(error)
}
}
// iterate, translate, reassign
const iterateAndTranslate = async (data) => {
for(key in data) {
if (typeof data[key] === 'object' && data[key] !== null) {
iterateAndTranslate(data[key])
} else{
data[key] = await translateText(data[key], 'fr')
}
}
}
//translate method
const translateText = async (text, targetLanguage) => {
try {
let [response] = await translate.translate(text, targetLanguage)
return response
} catch (error) {
console.log(error)
return 0
}
}
const writeJSONToFile = async () => {
var outputFileName = 'C:\\test\\test.json'
await fs.writeFileSync(outputFileName, JSON.stringify(data,null,4), (err) => {
if(err) {
console.log(err)
} else {
console.log('Done!')
}
})
}
// start from here
readJSONFile()
You have a few issues with your code.
Your functions use a global variable and mutate it instead of getting input and returning output.
timeout will cause unexpected behavior in your case.
you are using var
you have redundant async-await on the writeJSONToFile function
See my view of point about the possible solution.
const fs = require("fs");
const { Translate } = require("#google-cloud/translate").v2;
require("dotenv").config();
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
const credentials = JSON.parse(process.env.credentials);
const translate = new Translate({
credentials,
projectId: credentials.project_id,
});
// writeJSONTofile should be executed only after readJSONFile execution is completed
//read file
const readJSONFile = async () => {
try {
const data = JSON.parse(fs.readFileSync("...\\locale\\en.json"));
return iterateAndTranslate(data);
} catch (error) {
console.log(error);
}
return {};
};
// iterate, translate, reassign
const iterateAndTranslate = async (data) => {
for (let key in data) {
if (typeof data[key] === "object" && data[key] !== null) {
await iterateAndTranslate(data[key]);
} else {
data[key] = await translateText(data[key], "fr");
}
}
return data;
};
//translate method
const translateText = async (text, targetLanguage) => {
try {
let [response] = await translate.translate(text, targetLanguage);
return response;
} catch (error) {
console.log(error);
}
return null;
};
const writeJSONToFile = (data) => {
let outputFileName = "C:\\test\\test.json";
fs.writeFileSync(outputFileName, JSON.stringify(data, null, 4), (err) => {
if (err) {
console.log(err);
} else {
console.log("Done!");
}
});
};
// start from here
const run = async () => {
const data = await readJSONFile();
writeJSONToFile(data);
};
run();
See more:
why not using global varible
why not using var
this is my code:
const sendBoardId = async boardId => {
let result;
try {
result = await axios.get(`${fetchConfig.prefix}/game/get_board_by_id`, {
params: {
boardId,
},
});
} catch (e) {
console.log(e);
}
console.log('board:', result.data);
if (result.data == null || result.data.length == 0 || result.data == undefined) {
const BoardData = [];
return BoardData;
}
return result.data;
};
const Board = sendBoardId(1);
console.log('aaa', Board);
export { Board };
In the "aaa" console log I keep getting promise, and only in the promise result I got what I needed,
I want to export the promise result and not all the promise it self.
How should I do it?
To use Promise
const sendBoardId = async (boardId) =>
new Promise(async (resolve, reject) => {
let result;
try {
result = await axios.get(`${fetchConfig.prefix}/game/get_board_by_id`, {
params: {
boardId,
},
});
} catch (e) {
console.log(e);
reject(e);
}
console.log("board:", result.data);
if (
result.data === null ||
result.data.length === 0 ||
result.data === undefined
) {
resolve([]);
}
resolve(result.data);
});
const Board = sendBoardId; // UseLess
console.log("aaa", Board);
export { Board }; // Better to direct export sendBoardId
And use:
import {Board} from "...........";
const xyz = async() =>{
const data = await Board(1);
}
I am trying to export the value with instrument variable. however data is returning as [object Promise] than object. How can I assign module variable with the final result rather than the promise object.
var instruments = {
data: async () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." }
);
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols().then((data) => {
console.log('data');
return data;
}).catch((e) => {
console.log('error');
return {}
});
It looks like you want a singleton cache. Here is a basic implementation
cache.js
let data = {}
module.exports = {
getData: () => {
return data
},
setData: newData => {
data = newData
return
},
}
No need for async here. I would separate this code with the code that retrieves data.
fetchData.js
const cache = require('./cache')
const fetchData = () => {} // fetch data code here
fetchData().then(data => {
cache.setData(data)
})
try this
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." });
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols;
then import instrument method to call and then call
const instrument = require("./filepath");
instrument().then((data) => {
console.log('data');
}).catch((e) => {
console.log(e);
});
If your async function instruments.data() called, it'll await return Promise.
just append await at return for your expected result.
var instruments = {
data: async () => {
return await new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}
or remove async. it's same as above.
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}
I have a function
getAllSymbolsConfig = async reels => {
const symbols = new Set();
for (const column of reels) {
for (const symbol of column.symbols) {
symbols.add(symbol);
}
}
const coords = {};
for (const symbol of symbols) {
const response = await fetch(
`${this.props.gameDir}icons/${symbol}.json`
);
const json = await response.json();
coords[symbol] =
json.frames.find(frame => frame.filename.includes("active_0000")) ||
json.frames.find(frame => frame.filename.includes("active")) ||
json.frames[0];
coords[symbol].meta = json.meta;
}
return coords;
};
where it tries to fetch data from url ${this.props.gameDir}icons/${symbol}.json
However, in some situations ${symbol} variable is not available and I need to replace it with hardcoded url icon.json:
${this.props.gameDir}icons/icon.json
How would i retry the second fallback url if the first one fails?
Updated Try this:
function fetchData(url) {
await fetch(url).then((response) => {
return response;
})
}
let response = null;
response = fetchData(`${this.props.gameDir}icons/${symbol}.json`)
if (response.status == 400 ) {
response = fetchData(hardCodedUrl)
}
if (!!response) {
response.then((returnedResponse) => {
// Your response to manipulate
}).catch((error) => {
// Your error is here!
console.log(error)
});
}
I have a file with some functions:
const API = {
async getComments() {
var data = [];
var url = 'http://url.com/wp-json/wp/v2/comments';
const serviceResponse = await fetch(
url,
)
.then((serviceResponse) => {
return serviceResponse.json();
} )
.catch((error) => console.warn("fetch error:", error))
.then((serviceResponse) => {
for (i in serviceResponse) {
data.push({
"key": serviceResponse[i]['id'].toString(),
"name": serviceResponse[i]['author_name'],
"content": serviceResponse[i]['content']['rendered'].replace(/<(.|\n)*?>/g, ''),
"gravatar": serviceResponse[i]['author_avatar_urls']['96'],
"date": serviceResponse[i]['date'].replace('T', ' ')
});
}
global.comments = data;
return data;
});
}
}
export { API as default }
In another file I include the file, and make the call:
var comments = await API.getComments(key);
console.log (comments);
But I receive undefined, I tried to create a function with bind:
this.newGetComments = API.getComments.bind(this); with the same result. I used a global variable, but I want to remove the globals vars.
/* Few suggestions;
1. you are not returning anything from the getComments method. Please return fetch(url) response.
2. you should return something from the catch block to handle the error.
*/
const API = {
async getComments() {
var url = 'http://url.com/wp-json/wp/v2/comments';
var response = await fetch(url)
.then(serviceResponse => {
return serviceResponse.json();
})
.then(serviceResponse => {
let data = [];
// rest of your code.
return { success: true, data };
})
.catch(error => {
console.warn('fetch error:', error);
return { success: false, error };
});
return response
},
};
var comments = await API.getComments(key);
if (comments.success) {
// handle the success case comments.data
} else {
// handle the Error case comments.error
}
console.log(comments);
An async function always returns a promise as you aren't returning anything from the getComments methods, the function automatically resolves and returns undefined(Promise.resolve(undefined)). You need to return serviceResponse from the getComments method to get the fetch api's response.
You can try something like below:
async componentDidMount() {
let data = [];
const result = await this.getComments();
const jsonData = await result.json();
console.log('Result ==> ', jsonData.serviceResponse);
jsonData.serviceResponse.map(data =>
data.push({
"key": data['id'].toString(),
"name": data[i]['author_name'],
"content": data[i]['content']['rendered'].replace(/<(.|\n)*?>/g, ''),
"gravatar": data[i]['author_avatar_urls']['96'],
"date": data[i]['date'].replace('T', ' ')
})
);
console.log('Data ==> ', data);
}
async getComments() {
const result = await fetch('http://url.com/wp-json/wp/v2/comments', {method: 'GET'});
return result;
}