How can I convert this async function to promise? - javascript

Hello I have this async function which retrieves user profile and repositories through github api and returns them in an object.
And I want to convert this to a promise based function by using promise chaining (without any helper methods).
async function getUser(user) {
const profileResponse = await fetch(`https://api.github.com/users/${user}`);
const profileData = await profileResponse.json();
const repoResponse = await fetch(`https://api.github.com/users/${user}/repos`);
const reposData = await repoResponse.json();
// returning an object with profile and data
return {
profile:profileData,
repos:repoData,
};
}
//Calling the function here.
getUser("abufattah").then((res) => console.log(res));
I have managed to get it done using two helper functions and promise.all() method.
But how can I achieve the same thing by using promise chaining without any helper functions.
//Helper function 1: returns a promise with user profile data.
function getUserProfile(user) {
return fetch(`https://api.github.com/users/${user}`)
.then((res) =>res.json());
}
//Helper function 2: returns a promise with user repositories data.
function getUserRepos(user) {
return fetch(`https://api.github.com/users/${user}/repos?per_page=5&sort=created`)
.then((res) => res.json());
}
//Main function
function getUserWithPromise(user) {
return new Promise((resolve) => {
let profile = getUserProfile(user);
let repos = getUserRepos(user);
Promise.all([profile, repos]).then((values) => {
resolve({ profile: values[0], repos: values[1] });
});
});
}
// calling the function here
getUserWithPromise("abufattah").then((res) => console.log(res));

Transformation of async/await syntax to .then() calls is quite mechanical, especially if it doesn't involve any control flow syntax (loops or conditionals):
function getUser(user) {
return fetch(`https://api.github.com/users/${user}`).then(profileResponse => {
return profileResponse.json().then(profileData => {
return fetch(`https://api.github.com/users/${user}/repos`).then(repoResponse => {
return repoResponse.json().then(reposData => {
// returning an object with profile and data
return {
profile:profileData,
repos:repoData,
};
});
});
});
});
}
But there's no good reason to write code like that. If it's just that your target environment does not support async/await, let a transpiler do the transformation.

You can:
//Main function
function getUserWithPromise(user) {
return Promise.all([
fetch(`https://api.github.com/users/${user}`).then((res) =>res.json()),
fetch(`https://api.github.com/users/${user}/repos?per_page=5&sort=created`).then((res) => res.json())
]).then(([result1, result2]) => ({ profile: result1, repos: result2 }));
}
// calling the function here
getUserWithPromise("abufattah").then((res) => console.log(res));
Chain:
function getUserWithPromise(user) {
return fetch(`https://api.github.com/users/${user}`)
.then((res) => {
return fetch(`https://api.github.com/users/${user}/repos?per_page=5&sort=created`).then((fetch2Result) => ([res.json(), fetch2Result.json()]))
}).then(([result1, result2]) => ({ profile: result1, repos: result2 }))
}
// calling the function here
getUserWithPromise("abufattah").then((res) => console.log(res));

Related

Using Async Funtions in a For loop

I have an array where I need to call an API endpoint for each index. Once that is resolved I need to append it in that element. I want to return the updated array once this gets completed for each index of the array.
I tried using async-await in this way
// Let input be [{'x': 1, 'y': 2}, {'x': 11, 'y': 22}, ...]
async function hello(input) {
await input.forEach(element => {
fetch(url, options)
.then((res) => {
element['z'] = res
})
})
return input
}
I need to use this function to update my state
hello(data)
.then((res: any) => {
this.setState((prevState) => ({
...prevState,
inputData: res,
}))
})
The issue is that I need one more forced render for key 'z' to show.
How to resolve this?
I don't have much experience using async await so I am not sure if I am using it correctly.
The correct way is to use Promise.all and return the promise to be used by the caller function since you want the entire updated input value to be set in state.
In your case forEach doesn't return a promise so await on it is useless.
Also if you use await within the forEach function, you need to be able provide away to let the hello function's .then method call when all promises have resolved. Promise.all does that for you
function hello(input) {
const promises = [];
input.forEach(element => {
promises.push(
fetch(url, options)
.then(res => res.json()
.then((result) => {
// return the updated object
return {...element, z: result};
})
)
});
return Promise.all(promises);
}
...
hello(data)
.then((res: any) => {
this.setState((prevState) => ({
...prevState,
inputData: res,
}))
})
P.S. Note that the response from fetch also will need to be called with res.json()
async/await won't work in loops which uses callback(forEach, map, etc...)
You can achieve your result using for..of loop.
Try this and let me know if it works.
function getResult() {
return new Promise((resolve) => {
fetch(url, options)
.then((res) => {
return resolve(res);
})
})
}
async function hello(input) {
for (let element of input) {
let res = await getResult(element);
element['z'] = res;
}
}

How to return a variable along with the promise from a javascript function?

This is where I am stuck:
var verifyEmp = async function () {
return 'Verified';
}
Employee.find(email, password)
.then((emp) => {
console.log(emp);
return verifyEmp();
})
.then((msg) => {
console.log({ verificationMsg: msg });
})
.catch((err) => {
console.log(err);
})
As you can see, verifyEmp is a promise returning function(For demo purposes, I have kept this function as simple as possible). So, what I want to achieve is to be able to log { Emp: emp, verificationMsg: msg } in my second then. How do I pass emp variable in the second then while returning the promise.
I know this is conveniently achievable via async/await. I am just exploring how it can be done using traditional promises.
If you just want to use promises, you can nest a then() into the second one that resolves with the result of the first:
const verifyEmp = async function () {
return 'Verified';
}
const Employee = {
async find() {
return "An employee"
}
}
Employee.find()
.then(emp => verifyEmp().then(msg => [emp, msg]))
.then(([emp, msg]) => {
/* do something with amp and msg */
console.log({emp: emp, verificationMsg: msg });
})
.catch((err) => {
console.log(err);
})
You can return the Promise result from the verifyEmp().then() call (as async functions return a Promise) from the first find().then callback.
There you can pass both the result of the verifyEmp() call and also the emp object from the current scope wrapped in another object to the next then in the chain.
The Promise from the verifyEmp().then() gets automatically unwrapped in the next then callback in the chain:
var verifyEmp = async function () {
return 'Verified';
}
const Employee = {
find: async function(email, password){
return {email, password, "id":123};
}
}
Employee.find("test#abc.com", "test")
.then((emp) => {
//This promise woud get unwrapped in the next .then callback
return verifyEmp()
.then((msg) => ({emp,msg}));
})
.then((msg) => {
console.log(msg);
})
.catch((err) => {
console.error(err);
})
Didn't test it. Use an async iife or something of sort, if you hate anti-patterns. This solution will work regarless of verifyEmp returning a promise or not.
var verifyEmp = async function () {
return 'Verified';
}
Employee.find(email, password)
.then((emp) => {
console.log(emp);
return (async function(){return verifyEmp()})().then((ver)=>[emp,ver]);
})
.then((msg) => {
console.log({ verificationMsg: msg });
})
.catch((err) => {
console.log(err);
})
Just return json object instead of string.
var verifyEmp = async function () {
return 'Verified';
}
Employee.find(email, password)
.then((emp) => {
console.log(emp);
return {emp:emp, msg:verifyEmp()};
})
.then((res) => {
console.log({ Emp: res.emp, verificationMsg: res.msg });
})
.catch((err) => {
console.log(err);
})

I'm trying to use async/await to get a service, but the second service returns don't fill my variables

I have a service to get a list from server. But in this list I need to call another service to return the logo img, the service return ok, but my list remains empty. What i'm did wrong ?
I tried to use async/await in both services
I tried to use a separate function to get the logos later, but my html don't change.
async getOpportunitiesByPage(_searchQueryAdvanced: any = 'active:true') {
this.listaOportunidades = await this._opportunities
.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced)
.toPromise()
.then(result => {
this.totalSize = result['totalElements'];
return result['content'].map(async (opportunities: any) => {
opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']);
console.log(opportunities.logoDesktopUrl);
return { opportunities };
});
});
this.getTasks(this.totalSize);
}
No errors, just my html don't change.
in my
console.log(opportunities.logoDesktopUrl);
return undefined
but in the end return filled.
info:
Angular 7
server amazon aws.
await is used to wait for promise.
You should return promise from getBrand if you want to wait for it in getOpportunitiesByPage.
Change the getBrand function as following.
getBrand(brandsUuid): Observable<string> {
this.brandService.getById(brandsUuid).pipe(map(res => {
console.log(res.logoDesktopUrl); return res.logoDesktopUrl;
}))
}
Change opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']); to opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']).toPromise();
Please make sure you imported map from rxjs/operators.
At first,when you await, you should not use then.
At second, async/await runs only with Promises.
async getOpportunitiesByPage(_searchQueryAdvanced: any = 'active:true') {
const result = await this._opportunities
.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced)
.toPromise();
this.totalSize = result['totalElements'];
this.listaOportunidades = result['content'].map(async (opportunities: any) => {
opportunities.logoDesktopUrl = await this.getBrand(opportunities['brandsUuid']);
console.log(opportunities.logoDesktopUrl);
return opportunities;
});
this.getTasks(this.totalSize);
}
getBrand(brandsUuid) {
return new Promise((resolve, reject) => {
this.brandService.getById(brandsUuid).subscribe(res => {
console.log(res.logoDesktopUrl);
return resolve(res.logoDesktopUrl);
}, err => {
return reject(err);
});
});
}
But, because rxjs is a used in Angular, you should use it instead of async/await :
getOpportunitiesByPage: void(_searchQueryAdanced: any = 'active:true') {
this._opportunities.listaOportunidades(this.pageSize, this.currentPage, _searchQueryAdvanced).pipe(
tap(result => {
// we do that here because the original result will be "lost" after the next 'flatMap' operation
this.totalSize = result['totalElements'];
}),
// first, we create an array of observables then flatten it with flatMap
flatMap(result => result['content'].map(opportunities => this.getBrand(opportunities['brandsUuid']).pipe(
// merge logoDesktopUrl into opportunities object
map(logoDesktopUrl => ({...opportunities, ...{logoDesktopUrl}}))
)
),
// then we make each observable of flattened array complete
mergeAll(),
// then we wait for each observable to complete and push each result in an array
toArray()
).subscribe(
opportunitiesWithLogoUrl => {
this.listaOportunidades = opportunitiesWithLogoUrl;
this.getTasks(this.totalSize);
}, err => console.log(err)
);
}
getBrand(brandsUuid): Observable<string> {
return this.brandService.getById(brandsUuid).pipe(
map(res => res.logoDesktopUrl)
);
}
Here is a working example on stackblittz
There might be a simpler way to do it but it runs :-)

How to use promises instead of callback in reactjs?

i want to execute a function once the other function is completed. I have used callbacks but would like to use promises. But i am not sure how to go about it.
Below is the code,
this.set_function(this.save); //save is the callback method
set_function = (callback) => {
const some_var = {};
this.props.get_method.then(response => {
some_var.data = response;
this.setState({selected: some_var});
if(callback) {
callback(this.props.id, some_var_data);
}
});
};
save = (id, some_var) => {
const payload = {};
payload.some_var = [some_var];
client.update(id, payload)
.then((request) => {
this.save_data(id);
});
};
Here in the above code, once set_function is completed save function should be executed. As shown above it works with callback. How can i do the same with promises. Could someone help me with this?
The only trick there is that your callback expects two separate things (this.props.id and some_var_data). A promise can have only one fulfillment value — so you wrap those up as an object:
set_function = () => this.props.get_method.then(response => {
this.setState({selected: some_var});
return {id: this.props.id, data: response};
});
Notice that since you get a promise from this.props.get_method, we just chain off it.
(Your some_var_data was already an object, but it only had the data property, so I just included data in the result object directly.)
You'd use it like this:
set_function()
.then(({id, data}) => {
// use `id` and `data` here
})
.catch(error => {
// Handle error here
});
(Or don't include .catch and return the promise chain to something else that will handle errors.)
Or of course, if you used it in an async function:
const {id, data} = await set_function();
Make it promising, by returning the chained promise:
set_function = (callback) => {
return this.props.get_method.then(response => {
this.setState({selected: some_var});
return {id: this.props.id, some_var };
});
};
Then chain the other function:
this.set_function.then(this.save)
and finally desteucture the passed object:
save = ({ id, some_var }) => {
Await your promises
Using async ... await your function will return a promise that will resolve once your function is finished.
set_function = async () => {
const some_var = {};
const response = await this.props.get_method;
some_var.data = response;
this.setState({selected: some_var});
return [this.props.id, some_var_data];
};
When you call set_function it will return a promise so you can await or .then it. Like so:
this.set_function().then(this.save);
// where
save = ([ id, some_var ]) => {
...
}
In this case, you would have to have set_function return a Promise.
set_function = () => {
const some_var = {};
this.props.get_method.then(response => {
some_var.data = response;
this.setState({selected: some_var});
return Promise.resolve({id: this.props.id, some_var: some_var_data})
});
};
Now you can use it like this:
set_function().then(data => save(data)
Here's a jsfiddle you can play with.
https://jsfiddle.net/ctwLb3zf/

Returning a promise when using async/await in Firebase Cloud Functions

So I've happily been using async/await since node 8 is supported on Firebase Cloud Functions. I am struggling with 1 thing though. When using callable functions, it is told that you have to return a promise in the function, otherwise it won't work correctly. When using raw promises, its clear to me how to use it:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).then((result) => {
return result;
}).catch((err) => {
// handle err
})
});
But now, with async await, I'm not sure how to return this "chain of promises":
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
return res2;
// ??? Where to return the promise?
});
Does somebody know?
HTTP functions don't return a promise. They just send a result. You still have to use promises correctly in order to send the result, but a return value is not required. HTTP functions are terminated when the response is sent. See the documentation for more details:
Terminate HTTP functions with res.redirect(), res.send(), or res.end().
"await" is just syntax sugar for returning a Promise
When you write an async function, the code will actually exit the function and return a Promise at the first await it encounters. All code after the await will be converted to a then().
So for firebase writing code with async/await is perfectly save and in my experience even less error-prone, since I can more easily structure try&catch in my code!
Proof:
Just run this in your console:
async function iAmAsync() {
await new Promise(r => window.setTimeout(r, 1000))
return 'result'
}
let x = iAmAsync()
console.log(x)
Will print: Promise{<resolved>: "result"}
TL;DR: You don't need to change anything - if you write code with multiple awaits, this will be handled by firebase like a chain of promises and everything will just work.
And since my answer was downvoted, here is an authorative code-sample by the google firebase team itself:
https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase/functions/index.js
exports.addMessage = functions.https.onRequest(async (req, res) => {
// [END addMessageTrigger]
// Grab the text parameter.
const original = req.query.text;
// [START adminSdkPush]
// Push the new message into the Realtime Database using the Firebase Admin SDK.
const snapshot = await admin.database().ref('/messages').push({original: original});
// Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
res.redirect(303, snapshot.ref.toString());
// [END adminSdkPush]
});
You nailed it with your example code.
Async/await is just a newer way of promise. They can be used interchangeable.
Here is an example promise and async/await of the same function.
This
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).catch((err) => {
// handle error here
})
});
is equivalent to this:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
const result = await promiseMethod();
return nextPromise(result); // You are returning a promise here
}catch(e) {
// handle error here
}
});
Note that in both cases, you are returning a promise at the end. The return value of this onCall function would be whatever nextPromise(result) is. Since you are returning nextPromsie(result), you don't need to await it.
To see the code solution to your question look at the answer of dshukertjr.
If you want to understand how to return a "chain of promises" with async/await, here is your answer:
You cant !
Why ? Because await is used to wait for a Promise to complete. Once await return a value their is no more Promise.
So if you absolutely want to return a promise using await, you can wait for one of the two functions that return promises but not both.
Here is two way to do that:
A :
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
const result = await promiseMethod();
return nextPromise(result); // You are returning a promise here
}catch(e) {
// handle error here
}
});
B:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
return promiseMethod().then(async (result) => {
return await nextPromise(result);
}).catch((err) => {
// handle err
})
});
The only difference between A and B is that A waits for "PromiseMethod" to complete before returning a Promise. Whereas B returns a Promise right after being called.
Seems, we have to wait for several Promises in way like this:
const listOfAsyncJobs = [];
listOfAsyncJobs.push(createThumbnail(1, ...));
listOfAsyncJobs.push(createThumbnail(2, ...));
listOfAsyncJobs.push(createThumbnail(3, ...));
...
return Promise.all(listOfAsyncJobs); // This will ensure we wait for the end of the three aync tasks above.
From async method whatever you return it gets wrapped in promise automatically.
e.g
const myFun = async () => {return 5}
myFun();
// Output in the console
Promise {<fulfilled>: 5}
And you can chain with the returned result since it is a promise
Another example with enhancement as suggested in other answer
const myFun4 = async () => {
const myNum = await new Promise(r => window.setTimeout(() => r(5), 1000));
const myNum2 = await new Promise(r => window.setTimeout(() => r(5), 1000));
return myNum + myNum2;
}
myFun4().then((n) => console.log(n));
// Output
10
The return value of async-await function is Promise.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#return_value
So, what you did actually is returning a chain of promises.
const nextPromise = () => {
console.log('next promise!');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('next promise result')
}, 3000)
});
}
const promiseMethod = () => {
console.log('promise!');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise result');
}, 2000)
});
}
exports.createBankAccount = functions.https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).then((result) => {
return result;
}).catch((err) => {
// handle err
console.log(err);
})
});
exports.createBankAccountAsync = functions.https.onCall(async (data, context) => {
const result = await promiseMethod();
const res = await nextPromise(result);
return res;
});
I have created test project on firebase and both function calls give same logs.
A solution in that case is Promise.all().
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const promises = [];
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
promises.push(res1);
promises.push(res2);
// Here's the return of the promises
return Promise.all(promises).catch(error => console.error(error));
});
You may find more informations about promises in this article on freecodecamp.org/promise-all
Solution
For an alternative method, you can use Promise.allSettled(). It is the best way to wait for all promises in the function to complete as well as provide an easy way to modify the final return.
Excerpt from the documentation
The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
It is typically used when you have multiple asynchronous tasks that are not dependent on one another to complete successfully, or you'd always like to know the result of each promise.
Your updated code should be
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
return Promise.allSettled([res1, res2]).then((results) => results.forEach((result) => console.log(result.status)));
});
Additional Info
You should also look into Promise object, it has some nice methods for such a situation. Read more at documentation link
Since you need to return promise you can create the promise object and resolve/reject (return) your response from api after processing all the promises.
Option 1:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return new Promise(async (resolve, reject) => {
try {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
// This will return response from api
resolve(res2);
}
catch (err) {
// Handle error here
// This will return error from api
reject(err)
}
})
});
Option 2:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return new Promise(async (resolve, reject) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
// This will return response from api
resolve(res2);
})
.then((val) => val)
.catch((err) => {
// Handle error here
// This will return error from api
return err
})
});
Just convert to a Promise if required.
I.e. If nextPromise returns a Promise:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
return nextPromise(res1);
});
On the other hand, if nextPromise is an async function, just convert it to a Promise:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
return Promise.resolve(nextPromise(res1));
});
you can also convert the result:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
return Promise.resolve(res2);
});

Categories