I am making a function call from a useEffect, the function is present in a different class. From that function I am calling another async function to make the ajax call after the promise is resolved, I am performing some operation on the data returned from the API call and then I am returning the data back to the useEffect where the function is called in the first place. I am getting undefined in the useEffect. Here is the code snippet
// In Main Class
let newObj=new ABC();
useEffect(()=>{
let flag= newObj.method1(url); //method present in another class.
setFlag(flag)
});
// Inside Class ABC
let flag;
method1(url){
this.method2(url).then(function(result) {
/*some operation */
flag=true; //set to true based on data
console.log(flag)//prints true
}
return flag // console log will print false here.
}
async method2(url){
let data=await axios.get(url);
return data;
}
The end result I am trying to get in the main class is true/false based on the API call, I want all the logic to be present in the class ABC.
You need to make your method1 which will return the promise, currently it is returning normal value instead of promise so you are getting undefined. Make your function look like below.
method1{
return new Promise(function(resolve, reject) {
this.method2(url).then(function(result) {
/*some operation */
flag=true; //set to true based on data
console.log(flag)//prints true
}
resolve(flag) // console log will print false here.
});
}
and change parent function like
let newObj=new ABC();
useEffect(()=>{
newObj.method1(url).then(flag =>{
setFlag(flag)
})
});
useEffect(() => {
// Instead of using async at useEffect( async () => {}) Create a scoped async function in the hook
async function asynchronusFunction() {
await myFunction();
}
asynchronousFunction(); // call the above defined function now.
}, []);
You can create an async function call like the above method and use await to wait for the function to be exectuted. Using a async function means you are returning a promise. React does not wait for a promise : https://github.com/facebook/react/issues/1739
Related
I'm trying to write a "mixing" for JavaScript classes (controllers, in my app) to automatically "await" for a given function to be resolved, before actually invoke the real methods. Real class methods should receive the resolved value as last argument.
Here is the code of useAwait, where i'm looking for the static class property awaits and wrapping the originalFunc into a new async one. I'm calling the new function passing the original arguments plus the asyncFn call result:
const useAwait = (controller, asyncFn) => {
controller.constructor.awaits.forEach(func => {
const originalFunc = controller[func];
controller[func] = async (...args) => {
return originalFunc.apply(
controller,
[...args, await asyncFn.call(controller)]
);
};
});
}
So when useAwait(ctrl, this.load) is invoked on an instance this class:
class Controller {
static awaits = ['foo', 'bar'];
promise;
constructor() {
useAwait(this, this.load);
}
async foo(e, resolved) {
return resolved;
}
bar(resolved) {
return resolved;
}
async load() {
if (!this.promise) {
this.promise = new Promise(resolve => setTimeout(() => {
resolve('Hello World!');
}, 3000));
}
return this.promise;
}
}
The problem: all seems fine for foo (already async), but it's not for bar: the result is a Promise because now the bar is wrapped in async (wan't before). I know that an async function result is wrapped into a Promise. Codepen example where bar call outputs "[object Promise]".
So the question is: in theory, I should check if the original function is async and if it was not, await for it's return value?
...in theory, I should check if the original function is async and if it was not, await for it's return value?"
It wouldn't matter, your wrapper is async; an async function always returns a promise, whether you use await or not. Moreover, your wrapper can't be synchronous, because it needs to call awaitFn (load, in the example) and wait for its result.
If you're going to wrap originalFunction (bar) such that it waits for awaitFn (load) to complete, the wrapped version of it needs to be asynchronous (either async, or return a promise explicitly [or accept a callback, but better IMHO to use promises]). It cannot be synchronous, because awaitFn (load) isn't synchronous.
If the class instance isn't ready for use when you construct it, you might consider using a static method to get an instance instead; the static instance would return a promise that it fulfills with the instance once load is complete. Rough sketch:
class Controller {
dataFromLoadingProcess;
constructor(dataFromLoadingProcess) {
this.dataFromLoadingProcess = dataFromLoadingProcess;
}
async foo(e, resolved) {
// ...optionally use `this.dataFromLoadingProcess`...
return resolved;
}
bar(resolved) {
// ...optionally use `this.dataFromLoadingProcess`...
return resolved;
}
static async createInstance() {
await /*...the loading process...*/;
return new Controller(/*...data from loading process here, perhaps...*/)
}
}
I am trying to return some test data from a method that calls a rest api. To simulate the rest api call, I am adding some delay to return a promise using async-await pattern, but its not working as expected.
My understanding of async-await pattern in JavaScript is that any value returned from an async function is returned as a promise, so the value 100 should be returned as a promise by the function and therefore the last statement using .then should show 100, but it's not.
Question
What is wrong with the code snippet below which is causing the alert in last line of code to show undefined rather than 100?
async function f() {
function delayedResponse() {
setTimeout(function() { return 100}, 5000);
}
return await delayedResponse();
}
f().then(alert); // 100
You don't return anything in your delayedResponse so it's resulted in undefined.
Instead, in order to achieve what you expect - you can create a promise explicitly and resolve a value using timeout;
async function f() {
return new Promise(resolve => {
setTimeout(function() { resolve(100)}, 5000);
});
}
f().then(alert); // 100
NodeJS 14.x or Browser API
async function delay(msecs: number) {
return new Promise((resolve) => setTimeout(resolve, msecs));
}
NodeJS 16.x or later
import { setTimeout } from "timers/promises";
const result = await setTimeout(msecs, 'somevalue')
Note that the order of the setTimeout() arguments has changed!
The old setTimeout() is defined unless you import from "timers/promises".
References
Node 16 - Timers Promises API
For this project I am using wretch, which is a wrapper around fetch
This is a breakout of my code:
I have the wretch calls on a seperate service file to separate the render functionality from the wretch functionality.
I am exporting
export const wretchFunc = var => {
return wretch(url)
.json({
var,
})
.get()
.json()
};
The problem is with the second file: The function2 renders the result of the wretch function, I do resolve the promise and I get the result that I want but when I call it from function1 the return value is undefined even-though placing a console.log inside function2 gives me the boolean that I am looking for.
I have a function1 that returns:
return (function2 || function3)
and the function2 renders the result of wretchFunc and something like this:
function function2(state) {
const var = fun(state);
if (!var) {
return false;
}
wretchFunc(var).then(json => {
return JSON.parse(json).isTrue;
});
}
My assumption is that function one gets executed before receiving the result of the function2 but I am not sure what to do.
Edit:
I tried doing:
return wretchFunc(var).then(json => {
return JSON.parse(json).isTrue;
});
}
Which gives me another promise that I can resolve on the upper function, but since I have a previous condition if(!var) that returns false that would make the function return 2 different things. is there a way that I can cast the value of the promise in function2 and force function1 to wait for its result.
Thanks
Try wrapping into new Promise and returning it
function function2(state) {
return new Promise((res, rej) => {
const var = fun(state);
if (!var)
res(false);
wretchFunc(var)
.then(json => resolve(JSON.parse(json).isTrue)
.catch(err) => rej(err));
})
}
Hello Stack Overflow community,
I come to you with a problem related to JS async/await. I am trying to call an async function and then log the array to where the async function pushes the results to the console. If I call it like so directly in the console:
console.log(Page.data) - I can see that it has results in it, but if it is called on click of a button it logs an empty array.
// It is a nested object so do not worry if you don't exactly understand where Page.data comes from
Page.data = []
async function f1() {
// Fetch JSON data
// Process data
// Pushes at some point to the Page.data array
}
async function f2() {
// Fetch JSON data
// Process data
// Pushes at some point to the Page.data array
}
async function f3() {
// Fetch JSON data
// Process data
// Pushes at some point to the Page.data array
}
async function load(loader) {
let fn = async function() {};
if(condition1) fn = f1;
else if(condition2) fn = f2;
else fn = f3;
// This is the line that makes me problems
// According to documentation async functions return a promise
// So why would the array in the case be empty?
// Since I am telling it to display after the function is done
await fn(loader).then(console.log(Page.data))
}
This is just a template of my code and logic. I hope that you can understand where I am going.
Your help will be much appreciated.
The await expression causes async function execution to pause until a Promise is settled (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise.
For instance (this is an MDN example with some added comments):
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
// note that here, you are "awaiting" the RESOLVED RESULT of the promise.
// there is no need to "then" it.
var x = await resolveAfter2Seconds(10);
// the promise has now already returned a rejection or the resolved value.
console.log(x); // 10
}
f1();
So you would "await" your function, which would hold up the execution until the promise either resolves or rejects. After that line, you would run your console.log, and it would log as expected. Short answer, "remove the then".
I should add, if the result of the "awaited" function is not a promise, it is converted to a promise (so technically, there is no need to return a promise, it'll wrap up your returned value for you).
the problem is that you can use then with await for the same method, lets check some examples provided by MDN:
this is a working Promise using async/await:
let myFirstPromise = new Promise((resolve, reject) => {
setTimeout(function() {
resolve("Success!");
}, 250)
})
const functionCaller = async() => {
const result = await myFirstPromise
console.log("the result: ", result);
}
functionCaller();
what you are trying is:
let myFirstPromise = new Promise((resolve, reject) => {
setTimeout(function() {
resolve("Success!");
}, 250)
})
const functionCaller = async() => {
// you are not returning anything here... actually you are not doing anything with the response. despite it shows something, it is not sending any response
await myFirstPromise.then(console.log("something happened"))
}
// then this function doesn't really get a value.
functionCaller();
so what you need to do in your load call, is to change it like this:
async function load(loader) {
let fn = async function() {};
if(condition1) fn = f1;
else if(condition2) fn = f2;
else fn = f3;
return await fn(loader)
}
I'm confused of why do I need to run the async/await again in another function
Example
async function fetchData() {
try {
const result = await axios.get('http://example.com')
return result
} catch(err) {
return err
}
}
// and in another function I need to async await again
// if I want to get the value of fetchData()
// or else it will return pending promise object
function mapData() {
const data = fetchData()
console.log(data) // is a Pending Promise
}
If I want to get the data instead of Promise object
async function mapData() {
const data = await fetchData() // I still need to await??
console.log(data) // real value
}
I thought that If I already async/await in fetchData function it's already return a value instead of promise, why do I need to async/await again to get the actual data in mapData function?
An async function will always return a Promise - a Promise that resolves once the async function reaches its end (after any possible containing awaits). It doesn't magically make asynchronous code synchronous; any consumers of an async function will have to deal with its asynchronity as well.
It also usually makes more sense to handle errors in the consumer. Here, it doesn't look like you're doing anything special with the error in fetchData, so you can return the Promise immediately, without having to await it in the downstream function:
function fetchData() {
return axios.get('http://example.com');
}
async function mapData() {
try {
const data = await fetchData()
console.log(data)
} catch(e) {
console.log('Something went wrong...');
// handle failure of fetchData
}
}
Promises deal with time. They are a wrapper for a value that will be available eventually, BUT NOT YET. And you don't freeze your entire app untill the value is avilable.
async/await is just syntactic sugar over Promises. It lets you write code like
async function myFunction(){
var id = await asyncMethod1();
var value = await asyncMethod2(id);
return syncMethod3(id, value);
}
instead of
function myFunction(){
return asyncMethod1().then(id => asyncMethod2(id).then(value => syncMethod3(id, value)));
}
but it doesn't change how this thing works underneath.
So although syncMethod3 is sync the returned value still relies on the asyncronously computed values foo and bar. which makes myfunction async.
And every function that calls myFunction needs to wait untill myFunction has computed its value, ... and so on.