Reputation: 23
I'm looking for some suggestions on how to better structure my code when an asynchronous request is thrown into the mix. The following is an over-simplification of a problem I seem to keep running into.
Let's say I have an object that has three methods that do stuff. I have a load function that calls all three methods in order. partB shouldn't run until partA is done and partC shouldn't run until partB is done.
myObject = {
load: function() {
myOjbect.partA();
myObject.partB();
myObject.partC();
}
partA: function() {
// Do something here...
}
partB: function() {
// Do something here...
}
partC: function() {
// Do something here...
}
}
myObject.load();
But now there's a new requirement that I fetch some data in partA using an asynchronous call to an API, e.g. Google places:
partA: function() {
// Do something asynchronous
var placesService = new gm.places.PlacesService(map);
var request = { placeId: 'ABC' };
placesService.getDetails(request, function(results, status) {
placeDetails = JSON.stringify(results);
// Do something with 'results'
// Then do a few more things...
});
}
So now, I have to include the calls to partB and partC in the callback to the API. And now my load method is just a call to partA:
myObject = {
load: function() {
myOjbect.partA();
}
partA: function() {
// Do something asynchronous
var placesService = new google.map.places.PlacesService(map);
var request = { placeId: 'ABC' };
placesService.getDetails(request, function(results, status) {
// Do something with 'results'
// Then do a few more things...
// Then continue with Parts B and C...
myObject.partB();
myObject.partC();
});
}
partB: function() {
// Do something here...
}
partC: function() {
// Do something here...
}
}
Is there some way to avoid having to restructure my code this way, so that I'm not burying method calls within callbacks? Is this where using promises would make for cleaner code?
Upvotes: 2
Views: 474
Reputation: 6958
Yes, this is a case for either promises or async/await. Here is a good article highlighting the differences between both - https://hackernoon.com/should-i-use-promises-or-async-await-126ab5c98789
This is how your code would look like using promises:
myObject = {
load: function() {
partA().then( partB ).then( partC );
}
partA: function() {
return new Promise( function( resolve, reject ) {
// Do something here...
callAsManyPromissesAsYouLike().then( callSomeMorePromises ).then( function(){
// you can get as deep as you want with promises AND callbacks, as long as `resolve` is available:
placesService.getDetails(request, function(results, status) {
placeDetails = JSON.stringify(results);
// Do something with 'results'
// Then do a few more things...
resolve( 'this is an optional parameter that will be passed to partB (or the next chained promise)' ) // or reject( 'optional parameter to be passed to error handler ".catch()"' )
});
} )
} )
}
partB: function( responseFromA ) {
return new Promise( function( resolve, reject ) {
// Do something here...
resolve() // or reject()
} )
}
partC: function( responseFromB ) {
return new Promise( function( resolve, reject ) {
// Do something here...
resolve() // or reject()
} )
}
}
And using async/await:
myObject = {
load: async function() {
await myOjbect.partA();
myObject.partB();
myObject.partC();
}
partA: async function() {
// Do something here...
var callResult = await someAsyncCall()
// Do some more with callResult, or not
}
partB: function() {
// Do something here...
}
partC: function() {
// Do something here...
}
}
myObject.load();
Upvotes: 0
Reputation: 20236
The getDetails
method of PlacesService
works only with callbacks, it does not return a promise that you could handle with async/await.
So if you want to avoid nesting the calls to partB
and partC
using async/await, you have to make the callback return a promise, like this:
// faking the google API for demo
const google = {
map: {
places: {
PlacesService: class {
getDetails(req, cb) {
cb("RESULTS", "STATUS");
}
}
}
}
};
const map = "map";
const myObject = {
load() {
myObject.partA();
},
async partA() {
// Do something asynchronous
console.log("partA");
var placesService = new google.map.places.PlacesService(map);
var request = { placeId: "ABC" };
const { results, status } = await new Promise(resolve =>
placesService.getDetails(
request,
// pass a callback to getDetails that resolves the promise
(results, status) => resolve({ results, status })
)
);
this.partB(results, status);
this.partC(results, status);
},
partB(results, status) {
// Do something here...
console.log("partB", results, status);
},
partC(results, status) {
// Do something here...
console.log("partC", results, status);
}
};
myObject.load();
Upvotes: 1