function fakeRequest(url) {
return new Promise((resolve, reject) => {
delay = Math.floor(Math.random() * 4500) + 500;
setTimeout(() => {
if (delay > 4500) {
resolve(url + ": success")
}
else {
reject(url = ": error")
}
}, delay);
})
})
}
async function makeTwoRequests() {
let data1 = await fakeRequest("/page1");
console.log("Data 1:", data1)
let data2 = await fakeRequest("/page2");
console.log("Data 2:", data2)
}
makeTwoRequests()
When I remove data1 or data2 and just have 1 await it works. But when there are 2 or more it errors out saying: Uncaught (in promise) : error
I don't know what's happening here. Please help me out
Thanks!
If you await a promise that rejects, it becomes a thrown error.
Change the reject inside fakeRequest to a resolve and I bet the problem goes away.
This works for me:
function fakeRequest(url) {
return new Promise((resolve, reject) => {
let delay = Math.floor(Math.random() * 4500) + 500;
setTimeout(() => {
if (delay > 4500) {
resolve(url + ": success")
} else {
resolve(url + ": error")
}
}, delay);
})
}
async function makeTwoRequests() {
let data1 = await fakeRequest("/page1");
console.log("Data 1:", data1)
let data2 = await fakeRequest("/page2");
console.log("Data 2:", data2)
}
makeTwoRequests()
There were a couple other issues with the sample code:
delay ought to be declared with a keyword like let, const, or var; this is necessary to guarantee that each invocation of the function uses its own private version instead of a shared one, and to prevent this function from changing variables in the outer scope
the reject call uses = instead of +, which redefines and returns the url variable instead of concatenating the URL string with the literal : error string
there was an extra closing }), which prevented this code from parsing
But I think I get what you're up to: you're trying to simulate a situation in which API calls usually succeed, except for some that fail by timing out.
Your fakeRequest does accomplish that, but your calling code isn't capable of handling the problem. For that, you'd need something like this:
async function makeTwoRequests() {
let data1
try {
data1 = await fakeRequest("/page1");
} catch ( error ) {
console.error(`request 1 threw`, error)
// here, you might return, or re-throw, or fall through
}
let data2
try {
data2 = await fakeRequest("/page2");
} catch ( error ) {
console.error(`request 2 threw`, error)
// here, you might return, or re-throw, or fall through
}
}
Related
I am trying to make api call and store the results in array. function itself is an async. Here's a code.
async function setPoliciesData(policies) {
let tmpPolicies = {};
await policies.forEach((policy) => {
const tmp = { ...policy };
// Here I need help. This returns promise Instade remembers How to wait till promise finishes
tmp["Name"] = getPolicyNameFromLocalStrage(policy.id);
try {
if (policy?.audience?.id) {
tmp["members"] = getMembersFromAudienceId(policy.audience.id);
} else {
tmp["members"] = [];
}
} catch (e) {
console.log(e);
}
let id = policy.id;
console.log("Setting policy ID : " + policy.id);
tmpPolicies[policy.id] = tmp;
});
console.log("Done the processing");
return tmpPolicies;
}
I am getting Promise object in return. I would want members returnd array.
I tried to console log and I am seeing that issue seems to be because of method is not async. What is proper way to fix it.
I refactored some of your code, but if you should make the function inside of the forEach asynchronous. In this case, I changed it to map to be a bit easier to follow. The key at the end is to return Promise.all() which will wait for all of the inner promises to be resolved before returning:
async function setPoliciesData(policies) {
const tmpPolicies = policies.map(async (policy) => {
policy.Name = await getPolicyNameFromLocalStrage(policy.id);
try {
policy.members = policy.audience && policy.audience.id
? await getMembersFromAudienceId(policy.audience.id)
: [];
} catch (e) {
console.error('Error: ' + e);
}
return policy;
});
console.log("Done the processing");
return Promise.all(tmpPolicies);
}
I have a bit of Javascript code, which is not behaving as I'd expect it to. Can someone tell me what is going on here?
Here's a simplified version:
let recordsProcessed = 0
await parser(fileBuffer,
// Process row
async (row: Record<string, any>) => {
recordsProcessed += 1
try {
console.log('Processing record', recordsProcessed)
await processRow(row)
} catch (e) {
console.log('Failure at record', recordsProcessed)
}
}
)
async parser(fileBuffer: Buffer, rowCb: Function, ...) : Promise<number> {
...
return new Promise((resolve, reject) => {
parseFile(fileBuffer, options)
.on('error', (error:any) => reject(error))
.on('data', async row => await rowCb(row))
.on('end', (count: any) => resolve(count))
})
...
}
The parser() here is an async function, but it also calls some callbacks passed to it (I'm only showing one here, but there are multiple). It calls the rowCb() callback for each row in a file.
It's the try/catch block within the async callback which is not behaving as I'd expect. I'm using a test file, with three rows, which will cause every call to processRow() to throw an exception. So, I'd expect the output from the console.logs to be:
Processing record 1
Failure at record 1
Processing record 2
Failure at record 2
Processing record 3
Failure at record 3
But instead I'm getting this:
Processing record 1
Processing record 2
Processing record 3
Failure at record 3
Failure at record 3
Failure at record 3
Why is this happening? Since I'm awaiting processRow(), shouldn't it be in the same scope as the try/catch block, and therefore the catch() should be processed immediately after the processRow() throws an exception?
If it's processing multiple lines, parseFile() must have some loop inside. It's unclear if it's your code or it's coming from some library, but that loop either expects to work with asynchronous callbacks, or it doesn't. Perhaps those unshown options also affect this.
If it used a loop with await, the output would be what you expect:
async function thrower(i) {
throw "throwing " + i;
}
let somevariable = 0;
async function wrapper(i) {
try {
somevariable++;
console.log("calling", i, "(" + somevariable + ")");
await thrower(i);
} catch (x) {
console.log("caught", x, "(" + somevariable + ")");
}
}
(async function() {
for await (let i of [1, 2, 3]) // <-- async-aware loop
wrapper(i);
})()
However if it doesn't use await, then the loop progresses immediately when wrapper() encounters its own await line:
async function thrower(i) {
throw "throwing " + i;
}
let somevariable = 0;
async function wrapper(i) {
try {
somevariable++;
console.log("calling", i, "(" + somevariable + ")");
await thrower(i);
} catch (x) {
console.log("caught", x, "(" + somevariable + ")");
}
}
(async function() {
for (let i of [1, 2, 3]) // <-- async-unaware loop
wrapper(i);
})()
And if it's an ancient forEach(), then it doesn't matter even if it tries to await:
async function thrower(i) {
throw "throwing " + i;
}
let somevariable = 0;
async function wrapper(i) {
try {
somevariable++;
console.log("calling", i, "(" + somevariable + ")");
await thrower(i);
} catch (x) {
console.log("caught", x, "(" + somevariable + ")");
}
}
(async function() {
//[1, 2, 3].forEach(wrapper); // <- would be enough to produce the same output
[1, 2, 3].forEach(async function(i){
await wrapper(i); // <- absolutely futile attempt to wait,
// forEach just can't work asynchronously
});
})()
You need to add a new variable inside of the parser to get the number of the current record. You should use that variable instead of the global one.
let recordsProcessed = 0
await parser(fileBuffer,
// Process row
async (row: Record<string, any>) => {
recordsProcessed += 1
let thisRecordNum = recordsProcessed;
try {
console.log('Processing record', thisRecordNum)
await processRow(row)
} catch (e) {
console.log('Failure at record', thisRecordNum)
}
}
)
Hopefully someone can point me to the right direction. I read up on waiting for functions to complete before continuing and I resolved myself to using await/async but I am just stuck now.
I tried to get the Async/Await process to work, tried to inject the await in various locations, with adjusting the functions to be async, but i can not get the PSA_Resultbody to return to the original request. Any pointers would be appreciated.
Thank you,
CE
PSA_Resultbody = ProcessPSAAPI(xmlpackage, PSA_Action);
console.log("3 - Returned data:" + PSA_Resultbody);
calls the below:
async function ProcessPSAAPI(xmlpackage, PSA_Action) { //psa action is part of the options
var options = {...};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log('0 - Start '+body.toString());
if(res.statusCode != 200) {
PSA_Resultcode = "Error: " +res.statusCode +" - "+ res.statusMessage;
} else {
PSA_Resultcode = "Success: " +res.statusCode +" - "+ res.statusMessage;
PSA_Resultbody = ParseResults(body.toString()); //parse the results for later use --SCRIPT NEEDS TO WAIT FOR RESULTBODY TO COMPLETE
console.log("1 -PSA_Resultbody as part of RES = "+PSA_Resultbody);
}
});
res.on("error", function (error) {
console.error(error);
PSA_Resultcode = res.statusCode +" - "+ res.statusMessage;
});
});
console.log('2 -RESULT BODY BEFORE SENDING BACK TO INITIATING FUNCTION: '+PSA_Resultbody);
req.write(xmlpackage);
req.end();
return PSA_Resultbody;
Based on the above, my console log order is: 3,2,0,1 in stead of 0,1,2,3.
0 and 1 will have the correct data, so the API Call does work, but 2 will be "undefined" and should have the same data that is in 1.
There's no way to await an event emitter, so using async in this case isn't going to be useful. You cannot "return" from inside an event either.
The solution here is to return a new custom promise and to use resolve() inside of the "end" event of your emitter.
It will look something like this:
function ProcessPSAAPI(xmlpackage, PSA_Action) {
return new Promise( (resolve, reject) => {
// other code
res.on("end", function (chunk) {
// other code
resolve(PSA_Resultbody);
});
res.on("error", function (error) {
// other code
reject(error);
});
});
}
Here's a quick tutorial on creating your own promises, which I've written to simplify comprehension of the subject (official docs are somewhat dry and complex imho).
I did not change your code. I just put the appropriate promise structure in to get you started. This should really be a lesson in promises. async await is a shorthand promise structure. A Promise is one way you wait on code. It can be thought of as an array of callbacks that will be executed when the Promise is resolved.
A simple promise works like this:
const myPromise = new Promise(function(resolve, reject) {
/* Your logic goes in here. It can be anything.
* But the important part to remember is that when you have success, resolve it.
* When you have a failure, reject it.
*/
someCallBackPattern(function(error, data) {
if(error) {
reject(error);
} else {
resolve(data);
}
});
});
// To get the data out you use 'then', and 'catch'. then has two arguments.
myPromise.then(function(data) {
// The first argument is the result from resolve.
}, function(err) {
// The second argument is the result from reject.
}).catch((err) => {
// you can also get to the error from the catch callback
});
This is kinda messy and complex. So there is async await.
async function() {
try {
const result = await myFunctionThatReturnsAPromise();
// result is the resolved data
} catch (err) {
// err is the rejected Error
}
}
function myFunctionThatReturnsAPromise() {
return new Promise((resolve, reject) => {
// your code
})
}
And thats how it works.
async function someFunction () { // You can not wait on results unless you are in an await function
PSA_Resultbody = await ProcessPSAAPI(xmlpackage, PSA_Action); // await on your results.
console.log("3 - Returned data:" + PSA_Resultbody);
}
function ProcessPSAAPI(xmlpackage, PSA_Action) { // This does not need to be async. Unless you are awaiting in it.
return new Promise((resolve, reject) => { // async await is a shorthand promise structure. Although you do not need to use promises. It really helps to get the structure correct.
var options = {...};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log('0 - Start '+body.toString());
if(res.statusCode != 200) {
PSA_Resultcode = "Error: " +res.statusCode +" - "+ res.statusMessage;
reject(new Error(PSA_Resultcode)); // Reject you errors
} else {
PSA_Resultcode = "Success: " +res.statusCode +" - "+ res.statusMessage;
PSA_Resultbody = ParseResults(body.toString()); //parse the results for later use --SCRIPT NEEDS TO WAIT FOR RESULTBODY TO COMPLETE
console.log("1 -PSA_Resultbody as part of RES = "+PSA_Resultbody);
resolve(PSA_Resultbody); // Resolve your result
}
});
res.on("error", function (error) {
console.error(error);
PSA_Resultcode = res.statusCode +" - "+ res.statusMessage;
reject(new Error(PSA_Resultcode)); // Reject you errors
});
});
console.log('2 -RESULT BODY BEFORE SENDING BACK TO INITIATING FUNCTION: '+PSA_Resultbody);
req.write(xmlpackage);
req.end();
})
}
Question: With a Promise is there a mechanism to access the final return value in a .then() chain?
Background I need to run a series of checks/adjustments on an object. A few of the checks are asynchronous for example comparing data against stored data in mongodb. Because some checks are asynchronous I believe a Promise chain may be the correct option. However because the checks must be done in a specific order Promise.all() will not work. If a Promise chain is a possibility after all of the checks and adjustments have been made I'm not sure how to retrieve the object from the last .then() in the chain. Perhaps I'm approaching this problem with the wrong tool.
The following code is a simplified example. myObject passes through the chain of .then() statements but I'm not sure how to retrieve the final, updated object or if that's even possible.
function promisesPromises() {
return new Promise( function(resolve, reject) {
let x = {
name: 'super duper',
randomDataOne: 10000,
randomDataTwo: 5000
};
if (x) {
resolve(x);
} else {
reject('uh oh');
}
});
}
function firstAdjustment(myObject) {
myObject.randomDataOne += 1000;
return myObject
}
function secondAdjustment(myObject) {
myObject.randomDataTwo += 500;
return myObject;
}
promisesPromises()
.then(firstAdjustment)
.then(secondAdjustment);
I would give async/await a try to simplify things.
Note: async functions always return a promise. So in order to get to the data you need to await for the returned data inside another async function, or you need to use then(). I have given both examples in the code below.
function promisesPromises() {
return new Promise( function(resolve, reject) {
let x = {
name: 'super duper',
randomDataOne: 10000,
randomDataTwo: 5000
};
// Promise set to resolve after 5 seconds
if (x) {
setTimeout(() => {
resolve(x);
}, 5000)
} else {
reject('uh oh');
}
});
}
function firstAdjustment(myObject) {
myObject.randomDataOne += 1000;
return myObject
}
function secondAdjustment(myObject) {
myObject.randomDataTwo += 500;
return myObject;
}
const asyncFunction = async () => {
const myObject = await promisesPromises()
firstAdjustment(myObject)
secondAdjustment(myObject)
return myObject
}
// Example retrieving data with then()
asyncFunction().then(res => console.log("Example using then(): ", res))
// Example retrieving data inside async function
const anotherAsyncFunction = async () => {
const result = await asyncFunction()
console.log("Example using async(): ", result)
}
anotherAsyncFunction()
// Timer countdown to promise resolve
let count = 1
const timer = setInterval(() => {
console.log(count++)
if (count > 4) clearInterval(timer)
}, 1000)
I'm doing a recursive request with async/await whenever the received response has length === 0. The problem is that when some request returns the desired data, the resolve(data); part of the promise doesn't seem to work.
So, in my code, I have reached the point where I get to make multiple recursive calls and, finally, receive a response whose length is not 0.
Note: there are plenty of API keys published in Github if you want to test the code.
var apiKey = "yourApiKey";
var url = "https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=";
function requestData(url) {
return fetch(url).then(response => {
if(response.ok) {
return response.json().then(data => {
return Promise.resolve(data);
});
} else {
return Promise.reject(response.status);
}
});
}
function NasaRequest(sun, limit, frecuency) {
return new Promise(async (resolve, reject) => {
var data = await requestData(url + sun + "&api_key=" + apiKey);
if(data.photos.length === 0 && !limit) {
setTimeout(async () => {
console.log("Delay for next request (sun " + sun + "): ", frecuency);
return await NasaRequest(sun - 1, limit, frecuency);
}, frecuency);
} else {
console.log("Resolve data:", data); // Code acutally reaches this point
resolve(data); // But this doesn't seem to work
}
});
};
async function init() {
try {
const currentValue = await NasaRequest(2175, false, 2000);
console.log("currentValue:", currentValue); // I want to reach this point, but is like the promise never returns
}catch(err){
console.error(err);
}
}
init();
In that moment, I want to return the data in the response to the calling init() function, for what I use resolve(data);. But it doesn't seem to work.
What am I doing wrong?
The problem is on setTimeout. When you calling setTimeout it returns right away and implicitly return undefined. The subsequence return doesn't matter at that point. If all you want to do, is to pause, and then proceed try something like this
async function requestData(url) {
var response = await fetch(url);
if (response.ok) {
return response.json()
} else {
throw new Error(response.status);
}
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function nasaRequest(sun, limit, freq) {
var data = await requestData(url + sun + "&api_key=" + apiKey);
if (data.photos.length === 0 && !limit) {
await sleep(freq);
console.log("Delay for next request (sun " + sun + "): ", freq);
return await nasaRequest(sun - 1, limit, freq);
} else {
console.log("Resolve data:", data);
return data;
}
};
async function init() {
try {
const currentValue = await nasaRequest(2175, false, 2000);
console.log("currentValue:", currentValue);
} catch (err) {
console.error(err);
}
}
init();
I added a simple sleep function to handle the pause. I also modified requestData (removed then and Promise parts).
Note that using this recursive approach you may run into stack overflow. To avoid that problem you can simply use a loop and check against your limit variable.