I have a function which is making an async call
and upon sucess, its making another async call
ex:-
function1 = () => {
/*Some required Logic*/
return fetch("myurl")
.then((json) => {
function2(json)
})
.catch((error) => {
console.log(error)
})
}
function2 = () => {
/*Some required Logic*/
return fetch("myurl2")
.then((json) => {
callthirdfunction(json)
})
.catch((error) => {
console.log(error)
})
}
now here is the function 3
in which i am dependent on the success of function1
function3 = () => {
/*Some required Logic*/
function1().then(() => {
})
}
Issue is it is only waiting untill the function1's asyc call is succeeded its not waiting for function2's async call to succeed
I know i can write like chain of asyn call but its not possible because of unavoidable circumstances
Any suggesstion or leads in this regard would be of great help
If you used async await as you mentioned in your tags, you could just use it like that:
await function1();
await function2();
await function3();
while adding the async keyword to the function declarations:
function1 = async () => {
}
Another option is to promisify your functions, and then call them like that:
function1()
.then(function2)
.then(function3)
.catch() {}
If you want one promise to depend on another promise, you need to return the dependent promise from then.
Your code doesn't do anything with the result of function2:
return fetch("myurl")
.then((json) => {
function2(json)
})
That should read
.then((json) => {
return function2(json)
})
The whole point of then is that it allows chaining of subsequent promises by returning them from the callback.
Promises get chained only if you return them. In the below example
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function add1(x) {
const a = await resolveAfter2Seconds(20);
const b = await resolveAfter2Seconds(30);
return x + a + b;
}
Notice the return keyword inside resolveAfter2Seconds. Make sure that when you call a method that fires a promise, you add the 'return' keyword
Related
I'm new in React and I'm trying to call an async function, but I get the Unexpected reserved word 'await'. ERROR
So my async function is in a Helper class who is supposed to do all the API call, and I'm calling this Helper class in my Game function
Here is my code from Class Helper :
class Helper {
constructor() {
this.state = {
movieAnswer: {},
profile_path:"",
poster_path:"",
myInit: { method: "GET", mode: "cors" }
}
}
fetchMovieFunction = async (randomMovie) => {
return new Promise((resolve) => {
fetch(`${MOVIE_KEY}${randomMovie}?api_key=${API_KEY}`, this.myInit)
.then(error => resolve({ error }))
.then(response => resolve({ poster_path: response.poster_path }));
});
}
and here is where I call fetchMovieFunction in my game function :
const helper = new Helper();
function Game() {
useEffect(() => {
setRandomMovie(Math.floor(Math.random() * (500 - 1) + 1));
const { poster_path, error } = await helper.fetchMovieFunction(randomMovie)
if (error)
return console.log({error});
setApiMovieResponse({poster_path})
}, [page]);
return ();
}
And I don't understand why on this line const { poster_path, error } = await helper.fetchMovieFunction(randomMovie) I get the Unexpected reserved word 'await'. Error like my function is not declare as an async function
two issues:
You should use await inside an async function
But the callback passed to useEffect should not be async. So, you can create an IIFE and make it async:
useEffect(() => {
(async () => {
setRandomMovie(Math.floor(Math.random() * (500 - 1) + 1));
const { poster_path, error } = await helper.fetchMovieFunction(randomMovie);
if (error) return console.log({ error });
setApiMovieResponse({ poster_path });
})();
}, [page]);
The callback itself shouldn't be async because that would make the callback return a promise and React expects a non-async function that is typically used to do some cleanup.
You will necessarily need to wrap the hook callback logic into an async function and invoke it. React hook callbacks cannot be asynchronous, but they can call asynchronous functions.
Also, since React state updates are asynchronously processed, you can't set the randomMovie value and expect to use it on the next line in the same callback scope. Split this out into a separate useEffect to set the randomMovie state when the page dependency updates, and use the randomMovie as the dependency for the main effect.
useEffect(() => {
setRandomMovie(Math.floor(Math.random() * (500 - 1) + 1));
}, [page]);
useEffect(() => {
const getMovies = async () => { // <-- declare async
const { poster_path, error } = await helper.fetchMovieFunction(randomMovie);
if (error) {
console.log({error});
} else {
setApiMovieResponse({poster_path});
}
};
if (randomMovie) {
getMovies(); // <-- invoke
}
}, [randomMovie]);
FYI
fetch already returns a Promise, so wrapping it in a Promise is superfluous. Since you are returning the Promise chain there is also nothing to await. You can return the fetch (and Promise chain).
fetchMovieFunction = (randomMovie) => {
return fetch(`${MOVIE_KEY}${randomMovie}?api_key=${API_KEY}`, this.myInit)
.then(response => return {
poster_path: response.poster_path
})
.catch(error => return { error });
}
In order to do so, It's not suggested to asynchronize a function as a callback function being used in useEffect() since Effect callbacks are synchronous to prevent race conditions.
the better approach would be inside the useEffect callback
check this out:
useEffect(() => {
async function [name] () {
.....
.....
}
[name]();
},string[])
fetchMovieFunction doesn't need to be async because you're already returning a promise. Instead just return the fetch (also a promise), and catch any errors from that call in that method:
fetchMovieFunction = (randomMovie) => {
try {
return fetch(`${MOVIE_KEY}${randomMovie}?api_key=${API_KEY}`, this.myInit)
} catch(err) {
console.log(err);
}
});
But your useEffect, or rather the function you should be calling within the useEffect, does need to be async (because you're using await, but now you can just pick up the data from the fetch when it resolves.
useEffect(() => {
async function getData() {
setRandomMovie(Math.floor(Math.random() * (500 - 1) + 1));
const { poster_path } = await helper.fetchMovieFunction(randomMovie);
setApiMovieResponse({ poster_path });
}
getData();
}, [page]);
I have a problem with promises. The point is that the one function isn't completed and the executing code is jumping to then method.
const load = (function() {
const site = function(url, parent) {
return fetch(url)
.then(response => {
return response.text()
})
.then(data => {
parent.innerHTML = data;
})
};
return {
site: site,
};
})();
function mainApp() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log(document.querySelector('.content'));
const welcome = document.createElement('div');
const content = document.querySelector('.content');
welcome.textContent = `Welcome, ${user.displayName}`;
content.appendChild(welcome);
} else {
}
});
}
function promise() {
return new Promise((resolve, rejected) => {
load.site('pages/main/main.html', content);
//this function isn't completed and executing code is jumping to then
})
}
promise().then(() => {
console.log('then');
mainApp(); //this function is called before the upper code is completed
});
And because of this bug I can't change the DOM in mainApp function because this function is completed before the load function (which is also changing to DOM by using fetch). My goal is to firstly call and complete load.site() and then mainApp() but I don't know what's happening now and where is the problem.
I assume that you assumed that we assume that you resolved the function like
function promise() {
return new Promise((resolve, rejected) => {
load.site('pages/main/main.html', content);
resolve(); // Like this.
})
}
The Problem
When you call promise. It calls load.site and then resolves. So why isn't it giving the expected behavior.
Well, the problem is that load.site is a synchronous function, so it does not wait for the fetch call and returns which resolves promise before fetch is resolved. So, the code in promise.then() runs before the code in fetch.then()
The Solution
Make load.site an asynchronous function so that it returns a promise that can be awaited. i.e.
const site = async function (url) {
return fetch('a.js')
.then(response =>{
return response.text()
})
.then(data =>{
console.log("Loaded");
})
};
Then make the promise's executor async so that you await load.site. i.e.
function promise() {
return new Promise(async (resolve, rejected) => {
await load.site('pages/main/main.html'); // This line does the magic
resolve();
});
};
This will make sure that your promise resolves after fetch has resolved. So that you can get the expected behavior.
Hope it helps. Keep on coding
I have the following (simplified for the sake of scoping down problem) code:
function pushPromises() {
const promises = [];
promises.push(firstPromise('something'))
promises.push(secondPromise('somethingelse'))
return promises;
}
export default handlePromises(async (c) => {
const results = await Promise.all(pushPromises())
c.success(results);
});
My test mocks those firstPromise and secondPromise to check if they were called with the right arguments. This works (assume mock set up is properly done):
jest.mock('src/firstPromise');
jest.mock('src/secondPromise');
describe('test', () => {
let firstMock;
let secondMock;
beforeEach(() => {
require('src/firstPromise').default = firstMock;
require('src/secondPromise').default = secondMock;
})
it('test', async () => {
await handlePromises(context);
expect(firstPromiseMock).toHaveBeenCalledTimes(1);
expect(secondPromiseMock).toHaveBeenCalledTimes(1);
});
});
Now, if I add handlers to the promises such as:
function pushPromises() {
const promises = [];
promises.push(
firstPromise('something')
.then(r => console.log(r))
.catch(e => console.log(e))
)
promises.push(
secondPromise('somethingelse')
.then(r => console.log(r))
.catch(e => console.log(e))
)
return promises;
}
The first expectation passes, but the second one doesn't. It looks like the second promise is no longer called.
How can I modify the test/code so that adding handlers on the promises don't make the test break? It looks like it is just finishing the execution on the first promise handler and does never get to the second one.
EDIT:
I have modified the function to return a Promise.all without await:
export default handlePromises(async (c) => {
return Promise.all(pushPromises())
});
But I'm having the exact same issue. The second promise is not called if the first one has a .then.
In your edit, your handlePromises is still not a promise..
Try the following. ->
it('test', async () => {
await Promise.all(pushPromises());
expect(firstPromiseMock).toHaveBeenCalledTimes(1);
expect(secondPromiseMock).toHaveBeenCalledTimes(1);
});
Your handlePromises function accepts a callback but you are handling it as it returns a promise, it is not a good way to go but you can test your method using callback as following
Modify your Test as=>
it('test', (done) => {
handlePromises(context);
context.success = (results) => {
console.log(results)
expect(firstPromiseMock).toHaveBeenCalledTimes(1);
expect(secondPromiseMock).toHaveBeenCalledTimes(1);
done();
}
});
Or modify your code as =>
function pushPromises() {
const promises = [];
promises.push(firstPromise('something'))
promises.push(secondPromise('somethingelse'))
return promises;
}
export default handlePromises = (context) => {
return Promise.all(pushPromises()).then((data) => context.success(data))
};
//with async
export default handlePromises = async (context) => {
let data = await Promise.all(pushPromises());
context.success(data)
};
I have the following 2 functions, each returns a Promise:
const getToken = () => {
return new Promise((resolve, reject) => {
fs.readFile('token.txt', (err, data) => {
if (err) { return reject(err) }
if (!tokenIsExpired(data.token)) {
return resolve(data.token)
} else {
return requestNewToken()
}
})
})
}
const requestNewToken = () => {
return new Promise((resolve, reject) => {
restClient.get(url, (data, res) => {
fs.writeFile('tokenfile.txt', data.token, err => {
resolve(data.token)
})
})
})
}
function1()
.then(value => {
console.log('taco')
})
.catch(err => {
console.log(err)
})
So function1 runs, and (depending on some condition), it sometimes returns function2, which is returning another Promise. In this code, when function2 is called, the console.log('taco') never runs. Why is this? I thought that if you return a Promise from within a Promise, the resolved value of the nested Promise is what is resolved at the top level.
In order for me to get this to work, I have to do this:
const getToken = () => {
return new Promise((resolve, reject) => {
if (!tokenIsExpired()) {
return resolve(getToken())
} else {
return requestNewToken ()
.then(value => {
resolve(value)
})
}
})
}
That works, but it seems like I'm doing something wrong. It seems like there should be a more elegant way to handle/structure this.
You're right that promises auto-unwrap, but in this case you're returning from inside a promise constructor, which is ignored, you need to invoke either resolve or reject instead of using return. I think this might be the solution you're looking for:
const function1 = () => {
return new Promise((resolve, reject) => {
if (someCondition) {
resolve('foobar')
} else {
resolve(function2());
}
})
}
Inside a promise constructor, you need to call resolve or reject, which are equivalent to using return or throw from inside a then callback.
If you find this distinction confusing (I do), you should avoid the promise constructor entirely by just beginning a new chain with Promise.resolve, like this:
const function1 = () => {
return Promise.resolve().then(() => {
if (someCondition) {
return 'foobar';
} else {
return function2();
}
})
}
const function2 = () => {
return new Promise((resolve, reject) => {
resolve('hello world')
})
}
someCondition = false;
function1()
.then(value => {
console.log(value)
})
With your updated code, I recommend using a library to wrap APIs, rather than accessing the promise constructor yourself. For example, using bluebird's promisify:
const bluebird = require('bluebird');
const readFile = bluebird.promisify(fs.readFile);
const writeFile = bluebird.promisify(fs.writeFile);
const getUrl = bluebird.promisify(restClient.get, {multiArgs:true});
const getToken = () => {
return readFile('token.txt')
.then((data) => {
if(!tokenIsExpired(data.token)) {
return data.token;
} else {
return requestNewToken();
}
});
};
const requestNewToken = () => {
return getUrl(url)
.then(([data, res]) => {
return writeFile('tokenFile.txt', data.token)
.then(() => data.token);
});
};
I've remained faithful to your source code, but I'll note there may be a bug to do with writing data.token, and later trying to read the token property in that file.
Why use a library instead of the Promise constructor?
It allows you to write code which deals only with promises, which is (hopefully) easier to understand
You can be confident that callback APIs are correctly converted without losing errors. For example, if your tokenIsExpired function throws an error, using your promise constructor code, it would be lost. You would need to wrap all of your inner callback code in try {} catch(e) {reject(e)}, which is a hassle and easily forgotten.
I've got multiple promise' that I want to run one after the other, and I'm not sure I want to be returning the promises as it gets pretty messy!
So I decided to use the async library and implement the parallel method. Now I noticed that all my promises weren't running one, after the other, instead they were doing what promises are suppose todo (run + finish whenever).
I noticed that all the console.logs were running before all the promises were finished.
async.parallel([
(cb) => {
console.log ("hi")
grabMeData (Args)
.then ( (data) => {
// The promise is done and now I want to goto the next functio
cb();
}).catch(()=>console.log('err'));
},
(callback) => {
// The above promise is done, and I'm the callback
Query.checkUserExists()
.then ( () => {
if (Query.error) {
console.log (Query.error); // Determine error here
return; // Return to client if needed
}
callback();
});
},
() => {
// The above promise is done and I'm the callback!
// Originally wanted to be async
if (Query.accAlreadyCreated) {
this.NewUserModel.user_id = Query.user_id;
this.generateToken();
} else {
console.log ("account not created");
}
console.log ('xx')
}
], () =>{
console.log ("finished async parallel")
});
Any reason why my callbacks are being run before the promises are resolved (.then).
like Bergi said, async.js is redundant when you use promise, your code can be simplified as:
console.log('hi')
grabMeData(Args)
.catch(e => console.error(e))
.then(() => Query.checkUserExists())
.then(() => {
if (Query.accAlreadyCreated) {
this.NewUserModel.user_id = Query.user_id
return this.generateToken()
}
console.log ("account not created")
})
.then(() => console.log ('xx') )