How to access an object from another function in Javascript - javascript

I'm trying to get some data from an array and store store it in an object but I keep getting either an empty object or Promise { <pending> } in the logs. I'm using a global variable to store it and access it in another function. Not sure what i'm doing wrong.
var messageData = {};
const getNotifications = async () => {
let fcmObjects = await fcmTokens();
fcmObjects.forEach( (notificationData) => {
messageData = notificationData;
});
};
function getMessageData() {
console.log(messageData);
}
getMessageData();
getNotifications().then(function () {
console.log('All Done');
}).catch(function (error) {
console.log('Oops', error);
});

the console log is happening before the async method/Promise is being resolved. You should check it only after that. In other words, your code should be similar to:
getNotifications().then(function () {
getMessageData();
console.log('All Done');
}).catch(function (error) {
console.log('Oops', error);
});
If you do not want to call it inside getNotifications(), just get the Promise it returns and execute your call inside the .then() (option 1) or do an await (option 2). In code:
const notificationPromise = getNotifications();
// option 1
notificationPromise.then(getMessageData);
// option 2
await notificationPromise;
getMessageData();
A link to know more https://javascript.info/async about the topic.

Decoding your program line by line
var messageData = {};
is an object
const getNotifications = async () => {
let fcmObjects = await fcmTokens();
fcmObjects.forEach( (notificationData) => {
messageData = notificationData;
});
};
getNotifications is an async function.
function getMessageData() {
console.log(messageData);
}
getMessageData prints whatever is message data.
getMessageData();
you printed message data which is {}. Remember getNotfications is never called till now as the line are executed one by one.
getNotifications().then(function () {
console.log('All Done');
}).catch(function (error) {
console.log('Oops', error);
});
Now the above code call getNotification and run the function provided in then when the async call is completed. So you need to call getMessageData() in then function.
getNotifications().then(function () {
getMessageData();
console.log('All Done');
}).catch(function (error) {
console.log('Oops', error);
});

You execute getMessageData before getNotifications. You can use async/await approach
try {
await getNotifications();
getMessageData();
console.log('All Done');
} catch (error) {
console.log('Oops', error);
}
var messageData = [];
const getNotifications = async() => {
//let fcmObjects = await fcmTokens();
//fcmObjects.forEach((notificationData) => {
// messageData = notificationData;
//});
// simulate commentet above code
return new Promise((resolve,reject)=>{ messageData=['someMessage']; resolve()})
};
function getMessageData() {
console.log(messageData);
}
async function run() {
try {
await getNotifications();
getMessageData();
console.log('All Done');
} catch (error) {
console.log('Oops', error);
}
}
run();

You need to wait the getNotifications function to finish before you could log it's result to the console.
getNotifications is asynchronous, which means it won't run synchronously and the following lines of code
function getMessageData() {
console.log(messageData);
}
getMessageData();
may be executed before your getNotifications completes, thus your getMessageData() call doesn't print the wanted result.

First: your global variable messageData gonna be the last item in fcmObjects in this scenario. so make sure you provide a key or index for messageData object in fcmObjects.forEach( (notificationData) => {
messageData = notificationData;
});
Second:
when you call an Asynchronous operation you would not log it that way.
After all your code must be like this:
var messageData = {};
const getNotifications = async () => {
let fcmObjects = await fcmTokens();
fcmObjects.forEach( (index, notificationData) => {
messageData[index] = notificationData;
});
};
function getMessageData() {
console.log(messageData);
}
getNotifications().then(function () {
getMessageData();
console.log('All Done');
}).catch(function (error) {
console.log('Oops', error);
});

Related

Variable undefined after 2nd call

In the 1st function 'get_files()' I can log the file_list variable, it is correct here, however when I log it again in my 2nd function 'get_diffs()' it is undefined..
// Get files
async function get_files() {
await fs.readdir(dirPath, function (err, files) {
(async () => {
if (await err) {
console.log("Error getting directory information.", err)
} else {
var file_list = []; // Reset
await files.forEach(function (file) {
file_list.push(file);
});
console.log('1st Call = ' + file_list); // Correct
return await file_list;
}
})();
});
}
// Get Diffs
async function get_diffs() {
const file_list = await get_files();
console.log('2nd Call = ' + file_list); // Undefined
const dates = await get_dates();
return await files.filter(x => !dates.includes(x));
}
You have misunderstood async/await. Learn the basics here
function get_files() {
return new Promise((resolve, reject) => {
fs.readdir(dirPath, function (err, files) {
if (err) {
reject(err);
} else {
var file_list = []; // Reset
files.forEach(function (file) {
file_list.push(file);
});
console.log('1st Call = ' + file_list); // Correct
resolve(file_list);
}
});
})
}
fs.readdir does not return a promise. Use the promise based function fs.promise.readdir instead.
async function get_diffs() {
const file_list = await fs.promise.readdir(dirPath);
// ...
}
So you don't really need the other function. It had many problems anyway. await makes not much sense when used with an expression that is not a promise. All the places where you have used await in get_files, the expression that follows it does not represent a promise.

Confusion around 'nested' try/catch statements in Javascript

Essentially I have an async function containing a try/catch that calls another async function also containing a try catch, and I'm getting a bit confused about how to properly implement what I'm doing. Some "pseudocode" showing my current implementation:
const main = async () => {
try {
const test = await secondFunc();
console.log(test);
} catch(err) {
console.log('Found an error!');
console.log(err);
}
const secondFunc = async () => {
try {
await performSomeRequestExample();
} catch(err) {
if (err.x === 'x') {
doSomething();
} else {
//********
throw err;
//********
}
}
So what I'm trying to do is get the throw(err) (surrounded by the asterisks) to be caught by the catch in main() which will also call the console.log('Found an error!'), but what currently happens is the error is thrown from secondFunc(), the catch in main() is never hit and I get an unhandled promise rejection.
Any guidance on what I'm doing wrong?
My advice is to minimize using try/catch unless absolutely necessary. With async functions (or any functions that return a Promise object) you can usually simplify things by not worrying about try/catch blocks unless you need to do something specific with certain errors. You can also use .catch rather than try/catch blocks to make things easier to read.
For example your code above could be written like this:
const main = async () => {
const test = await secondFunc().catch(err => {
console.log("Found an error from secondFunc!", err);
throw err; // if you want to send it along to main's caller
});
if (test) {
console.log("Test", test);
}
};
const secondFunc = () => {
return performSomeRequestExample().catch(err => {
if (err.x === "x") {
doSomething();
} else {
throw err;
}
});
};
const performSomeRequestExample = () => Promise.reject("bad");
main().then(
() => console.log("worked"),
err => console.log("failed from main", err)
);
In secondFunc we don't need to use async since we can just return the promise coming back from performSomeRequestExample and handle any failures in the .catch.
You should use
const secondFunc = async () => {
performSomeRequestExample().then(res =>{
console.log(res);
})
.catch(err => {
console.log(err);
}
)
Add a return before the await of performSomeRequestExample.
const secondFunc = async () => {
try {
return await performSomeRequestExample();
} catch (err) {
if (err.x === 'x') {
console.log('x');
} else {
throw err;
}
}
}
or you can also use .catch() after the awaited function.
Another solution can be like this
const main = async() => {
try {
const test = await secondFunc();
console.log(test);
} catch(err) {
console.log('Found an error!');
console.log(err);
}
}
const secondFunc = async () => {
//return await performSomeRequestExample(); //for success
return await performSomeRequestExample(2); //for error
}
const performSomeRequestExample = async(abc=1) => {
return new Promise(function(resolve,reject){
if(abc ==1){
setInterval(resolve("yes"),400);
}else{
setInterval(reject("opps"),400);
}
});
}
main();
Test this code at this link:
https://repl.it/repls/JoyfulSomberTelevision

How can I run several async api functions, then call another function when all are complete?

I'm using a node script to make several api calls asynchronously and now i need to 'do something' once those calls are done. Not sure what to do in my createProject() function after i perform the request, set the options and get doSomething AFTER the options are set.
function performRequest(reqOptions, reqBody, success) {
const req = https.request(reqOptions, (res) => {
var chunks= [];
// data received
res.on('data', function (chunk) {...});
// data processed
res.on('end', function() { success(Buffer.concat(chunks).toString()); });
});
// error occurred during request
req.on('error', (e) => {
console.error('Request error: ' + e);
});
req.write(JSON.stringify(reqBody));
// end request
req.end();
}
function createProject(name) {
var reqOptions = {...};
var reqBody = {...};
performRequest(reqOptions , reqBody , function(data) {
// project successfully created - set project options
setOption1(data.id);
setOption2(data.id);
// after options set, do something
doSomething();
});
}
function setOption1(id) {
// performRequest(...)
}
function setOption2(id) {
// performRequest(...)
}
function doSomething() { ... }
We can make non-bloking function to blocking function using Promise, await, async
function performRequest(reqOptions, reqBody, success) {
return new Promise((resolve, reject) => {
const req = https.request(reqOptions, (res) => {
var chunks = [];
// data received
res.on('data', function (chunk) { });
// data processed
res.on('end', function () {
resolve(Buffer.concat(chunks).toString());
});
})
// error occurred during request
req.on('error', (e) => {
console.error('Request error: ' + e);
reject(e);
});
req.write(JSON.stringify(reqBody));
// end request
req.end();
});
}
async function createProject(name) {
var reqOptions = { ... };
var reqBody = { ... };
var data = await performRequest(reqOptions, reqBody);
// project successfully created - set project options
await setOption1(data.id);
await setOption2(data.id);
// after options set, do something
doSomething();
}
async function setOption1(id) {
return performRequest(...)
}
async function setOption2(id) {
return performRequest(...)
}
function doSomething() {
}

Async/Await function not waiting for promise to end

let myObject = ( () => {
let run = async() => {
foo = new genericFunction();
status = await foo.myFunction();
}
})();
Another FIle.js
let genericFunction = function() {
this.getData = async () => {
$.getJSON("path/to/file.json", function (data) {
console.log("Apple", data.name);
return data.name;
}).fail(function(jqxhr, textStatus, error){
console.log("Loading Error :: ", error);
)};
}
this.myFunction = async () => {
let data = this.getData();
console.log('DATAA:::', data); //This should be the first output
}
}
The problem is:
status is always = undefined because somehow it returns before getJSON executes and I don't know why.
Another FIle.js should be like:
let genericFunction = function() {
this.getData = async () => {
var result = await $.getJSON("path/to/file.json", function (data) {
console.log("Apple", data.name);
return data.name;
}).catch(function(jqxhr, textStatus, error){
console.log("Loading Error :: ", error);
)};
return result;
}
this.myFunction = async () => {
let data = this.getData();
console.log('DATAA:::', data); //This should be the first output
}
}
Here's a simplified example using your code but with a couple of changes:
1) Uses a class
2) You're not returning anything from myFunction so there's no reason to assign something to status - just call the function.
3) getData doesn't need an async keyword, and you need to return the promise (in this example I mocked up a AJAX call that returns data after a second) from the function for it to work.
4) You do need to await for the promise to return in myFunction. You have async there, you just need to add the await keyword too.
class GenericFunction {
getData() {
return new Promise(resolve => {
setTimeout(() => resolve('Hello World'), 2000);
});
}
async myFunction() {
let data = await this.getData();
console.log('DATAA:::', data);
}
}
(async () => {
const foo = new GenericFunction();
foo.myFunction();
})();

Paradoxical issue with mocha done() and async await

I have the following test case:
it("should pass the test", async function (done) {
await asyncFunction();
true.should.eq(true);
done();
});
Running it asserts:
Error: Resolution method is overspecified. Specify a callback or
return a Promise; not both.
And if I remove the done(); statement, it asserts:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure
"done()" is called; if returning a Promise, ensure it resolves.
How to solve this paradox?
You need to remove the done parameter as well, not just the call to it. Testing frameworks like Mocha look at the function's parameter list (or at least its arity) to know whether you're using done or similar.
Using Mocha 3.5.3, this works for me (had to change true.should.be(true) to assert.ok(true) as the former threw an error):
const assert = require('assert');
function asyncFunction() {
return new Promise(resolve => {
setTimeout(resolve, 10);
});
}
describe('Container', function() {
describe('Foo', function() {
it("should pass the test", async function () {
await asyncFunction();
assert.ok(true);
});
});
});
But if I add done:
describe('Container', function() {
describe('Foo', function() {
it("should pass the test", async function (done) { // <==== Here
await asyncFunction();
assert.ok(true);
});
});
});
...then I get
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
Removing done as a param from it worked for me! Instead only use expect/should. Example is as follows:
getResponse(unitData, function callBack(unit, error, data){ try {
return request.post(unit, function (err, resp) {
if (!err && resp.statusCode === 200) {
if (resp.body.error) {
return callback(obj, JSON.stringify(resp.body.error), null);
}
return callback(obj, null, resp);
} else {
if (err == null) {
err = { statusCode: resp.statusCode, error: 'Error occured.' };
}
return callback(obj, err, null);
}
});
} catch (err) {
return callback(obj, err, null);
}}
BEFORE:
it('receives successful response', async (done) => {
const getSomeData = await getResponse(unitData, function callBack(unit, error, data){
expect(data.statusCode).to.be.equal(200);
done();
}) })
AFTER (works):
it('receives successful response', async () => {
const getSomeData = await getResponse(unitData, function callBack(unit, error, data){
expect(data.statusCode).to.be.equal(200);
}) })
Sometimes there are cases you need to use async/await + done function in mocha.
For example, in one of my socket.io unit test cases, I have to call db functions with async functions and test socket event handlers which are callback functions:
context("on INIT_CHAT", ()=> {
it("should create a room", async (done) => {
const user = await factory.create("User");
socket.emit("INIT_CHAT", user);
socket.on("JOIN_CHAT", async (roomId) => {
const room = await ChatRoom.findByPk(roomId);
expect(room).to.exist;
// then I need to close a test case here
done();
});
});
});
This will causes the exact same error as in the OP:
Error: Resolution method is overspecified. Specify a callback or return a Promise; not both.
My Workaround:
I just wrapped the entire test code in a promise generator:
context("on INIT_CHAT", ()=> {
it("should create a room", async () => {
const asyncWrapper = () => {
return new Promise(async (resolve) => {
const user = await factory.create("User");
socket.emit("INIT_CHAT", user);
socket.on("JOIN_CHAT", async (roomId) => {
const room = await ChatRoom.findByPk(roomId);
expect(room).to.exist;
resolve(true);
});
});
});
await asyncWrapper();
});
});

Categories