How to use promises instead of callback in reactjs? - javascript

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/

Related

How can I convert this async function to promise?

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));

How to wait for a function, which contains promises

If we have this function
const getData = () => {
foo()
.then(result => {
return result;
})
.catch(error => {
return error;
});
};
Although getData is not a promise itself, but it contains a promise, which is asyncrnous.
So what is the best way to wait for getData to return something. Async / Await doesn't work cause they work with promises.
Thanks.
Currently, getData() doesn't return anything. You need to make it return a Promise, so you can await it or chain .then() to it.
const getData = () => {
return foo() // <-- here
.then(result => {
return result;
})
.catch(error => {
throw error;
});
};
// Now you can do :
getData().then(...)
// or :
const data = await getData();
In this case, you can also omit the curly braces and the explicit return, and make it implicit :
const getData = () => foo()
.then(result => {
return result;
})
.catch(error => {
throw error;
});
Hey but what's that :
.then(result => {
return result;
})
This does nothing. It takes a value and simply returns it without doing anything. You can remove it.
You can now rewrite getData() this way :
const getData = async () => {
try {
return await foo()
} catch (error) {
throw error;
}
}
For that matter, this :
.catch(error => { throw error; });
or this :
catch (error) { throw error; }
are also pretty useless, they just "relay" (bubble up) the error that has to be caught in the calling function.
Now it's obvious getData does pretty much only one thing, it's returning foo(), which is a Promise. It's only a wrapper around a Promise... so it's actually pretty useless.
Bottom line, detData() is useless altogether. foo is a Promise; writing a function that returns a Promise so you can use it like a Promise is just... a Promise with an extra step. Just use foo directly.
let result;
try {
result = await foo();
} catch (error) {
console.log(error);
}
console.log(result);
This will not work because getData is not returning a value. You can add a return statement before foo call and wait for the return value.
const getData = () => {
return foo();
};
getData().then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
To wait for an operation you must return a Promise or use a callback. The code snippet below runs and should illustrate how this works. I implemented a sample foo function that is actually asynchronous (wait for 1 second before returning the data '12345'). I used async/await to illustrate how that can work, but you can equally return the result of foo and use then instead.
const foo = () => {
return new Promise(resolve => {
setTimeout(() => resolve('12345'), 1000);
});
}
const getData = async () => {
const data = await foo();
console.log(`Data is ${data}`);
return data;
};
getData()
.then(() => console.log('complete'))
.catch(err => console.log(`oops: ${err}`));
console.log('this prints first since async operation is still pending');

Function gets trigger before promise is resolved

I have this two functions, writedb is supposed to act only when uploadImages is done. I'm not getting what I'm hoping for.
The resolve shows "null" for "this.imageURL" as is predefined but is triggered before the console.log that are inside the loop which show the correct wanted information.
uploadImages() {
return new Promise(async (resolve, reject) => {
let newImageURL = []
for (const path of this.images){
fs.readFile(path,
async (err, data) => {
const ref = st.ref(`images/${this.id}/${path.split('/').pop()}`);
await ref.put(data);
let url = await ref.getDownloadURL();
newImageURL.push(url);
this.imageURL = newImageURL;
console.log(this.imageURL);
})
}
resolve(console.log(this.name),
console.log(this.imageURL),
console.log('Done upload images'))
})
}
writedb(){
(() => {
let obj = {};
this.uploadImages().then(
console.log('writedb in action...'),
db.collection("products").doc(this.id).set(Object.assign(obj, this))
)
})();
}
What am I doing wrong here? How can i get the promise to resolve only if the for loop is done??
Use fs.promises and Promise.all to wait for each readFile to resolve - and avoid the explicit Promise construction antipattern:
uploadImages() {
return Promise.all(this.images.map(
path => fs.promises.readFile(async (data) => {
const ref = st.ref(`images/${this.id}/${path.split('/').pop()}`);
await ref.put(data);
return ref.getDownloadURL();
})
));
}
Then instance.uploadImages() will return a Promise that resolves to an array of the data you want. uploadImages().then((result) => { /* do stuff with result */ }).catch(handleErrors);
With CertainPerformance help I've corrected my code as follows
Thank you! After some though I finally was able to implement it and achieve the expected result.
uploadImages (){
return Promise.all(this.imagespath.map(
path => fs.promises.readFile(path)
.then(async (data)=>{
const ref = st.ref(`images/${this.id}/${path.split('/').pop()}`);
await ref.put(data);
return ref.getDownloadURL();
})
))
}
writedb(){
this.uploadImages()
.then((res)=>{
this.imageURL = res;
//Firebase no toma custom objects Object.assign(obj, this)
let obj ={}
Object.assign(obj, this);
delete obj.imagespath;
db.collection("products").doc(this.id).set(obj)
}).catch( err=>console.log(err) )
}

React setState callback return value

I'm new in React and I was looking to achieve this kind of flow:
// set the state
// execute a function `f` (an async one, which returns a promise)
// set the state again
// return the promise value from the previous function
So, what I'm doing now is the following:
async function handleSomething() {
this.setState((prevState) => { ... },
() => {
let result = await f()
this.setState((prevState) => { ... },
...
)
})
return result;
}
Hope you get the idea of what I want to achieve. Basically I want to get result, which is the value returned from awaiting f, and return it in handleSomething so I can use it in another place, but wrapping it up inside those setState calls:
// g()
// setState
// res = f()
// setState
// return res
My question is, how can I do this properly? Maybe should I modify the state with the result value and get it from there?.
EDIT:
Usage of handleSomething:
// inside some async function
let result = await handleSomething()
You can create a Promise that resolves once both setState calls are done:
function handleSomething() {
return new Promise(resolve => {
this.setState(
prevState => {
/*...*/
},
async () => {
let result = await f();
this.setState(
prevState => {
/*...*/
},
() => resolve(result)
// ^^^^^^^ resolve the promise with the final result
);
}
);
});
}
Which would be used like:
this.handleSomething().then(result => /* ... */)
// or
const result = await this.handleSomething();

What's the promise chaining equivalent of awaiting multiple async functions?

I'm studying the usage of promsies and async/await.
I've wrote the following code, which does the following:
It gets some database's data (using Knex.js),
Handles that data,
Assigns the handled data into a specified property.
These 3 steps are done multiple times (In the following code, it's done twice), and are always awaited:
async function run() {
return await getData();
}
async function getData() {
let handledData = {};
handledData.res1 = await knex.select('column1').from('table1').where('column1', '1')
.then(data => handleData(data))
.catch(handleError);
handledData.res2 = await knex.select('column1').from('table1').where('column1', '2')
.then(data => handleData(data, handledData))
.catch(handleError);
return handledData;
}
async function handleData(data) {
let res = [];
data.forEach(item => {
res.push(item.column1);
});
return res;
}
function handleError (error) {
console.log(error);
}
Now, I'm trying to write the promise-chaining equivalent of getData, and this is what I came up with:
async function getData() {
let handledData = {};
let promise = new Promise(function(resolve, error){ resolve(); });
promise
.then(function () {
return knex.select('column1').from('table1').where('column1', '1')
.then(data => handleData(data))
.catch(handleError);
})
.then(function(handled){
handledData.res1 = handled;
return knex.select('column1').from('table1').where('column1', '2')
.then(data => handleData(data))
.catch(handleError);
})
.then(function(handled){
handledData.res2 = handled;
return handledData;
})
.catch(handleError);
return promise;
}
But this doesn't quite work. What happens is that after the first then returns, the await inside run ends its awaiting, which causes run to return - and only then the second then is executed.
How can I make the promise-chaining version work as the multiple-await version does?
(and please, feel free to point out any misunderstaings I made of promises/async-await)
If possible, I'd recommend using Promise.all instead, it'll make your script run faster in addition to making the logic clearer:
const getData = Promise.all([
knex.select('column1').from('table1').where('column1', '1')
// Simply pass the function name as a parameter to the `.then`:
.then(handleData)
.catch(handleError),
knex.select('column1').from('table1').where('column1', '2')
.then(handleData)
.catch(handleError)
])
.then(([res1, res1]) => ({ res1, res2 }));
knex.select().then() returns a promise, so you don't need to wrap it in another promise you just need to set up the chain of then()s and return the whole thing. The result will be that getData returns the promise from the last then. You can return the value you want from that then() which will make it available to the caller. For example:
function run() {
getData()
.then(handledData => console.log(handledData) /* do something with data */)
}
function getData() {
let handledData = {};
// need to return this promise to callers can access it
return knex.select('column1').from('table1').where('column1', '1')
.then(data => handledData.res1 = handleData(data))
.then(() => knex.select('column1').from('table1').where('column1', '2'))
.then(data => {
handledData.res2 = handleData(data)
return handledData
})
.catch(handleError);
}
You could also set this up to pass the handledData object thought the chain, but you don't need to in this case.
The function handleData() is synchronous, so you don't need to make it an async function.

Categories