I have recently been assigned a ticket to integrate a 3rd party SDK library into our backend to query their api. The library isn’t open sourced as we had to get it from the team through the customer service and it’s a little outdated, with no disrespect. For professional reason I am not going to disclose the company’s name, but it mainly does identity verification.
The SDK came with a Client class with a list of methods, and a makeRequest method out of the box and each of the methods calls the makeRequest function, which takes a callback as one of its parameters, and directly return the result. The following is a simplified rewrite of it .
class Client{
...
serviceMethodOne(id, callback){
return this.makeRequest('get','/endpoint',callback)
}
...
... makeRequest(...., callback, ....){
...
...
callback(data)
...
}
}
From its short README the following is provided as an example to show how to use the SDK client.
client.serviceMethodOne((profile) => {
client.serviceMethodTwo(
profile.id,
....
}, (search) => {
client.serviceMethodThree(
search.id,
...
...
},(assessment) => {
/do something with assessment/
}
);
});
});
You can immediately tell that this is a callback hell and while I can do whatever I need inside the last callback from the above example, I got stuck on how to return the assessment data and use it outside the callback. If you directly return assessment as the following example, it will be undefined because assessment is not resolved.
....
const assessment = await client.serviceMethodOne((profile) => {
....
search.id,
...
...
},(assessment) => {
return assessment
}
);
});
});
With some thinkings and helps from some amazing people, I finally was reminded that I could just use Promise. The trick is to promisify the entire callback stacks and resolve the data for assessment and return the entire thing as a promise. The following is the example write up to show how to do that.
const promiseFunction = () =>
return new Promise((resolve, reject) => {
....
....
try {
client.serviceMethodOne((profile) => {
client.serviceMethodTwo(
profile.id,
....
}, (search) => {
client.serviceMethodThree(
search.id,
...
...
},(assessment) => {
return resolve(assessment);
}
);
});
});
} catch (error) {
return reject(error);
}
});
const assessment = await promiseFunction();
Voila, now assessment is now successfully returned. Now on top of this, we could promisify each of the callbacks therefore to avoid the callback hell we saw from the original code. This will look something like
const promisifiedCallbackOne = () => {
return new Promise((resolve, reject) => {
client.serviceMethodOne((profile, error) => {
if (error) reject(error);
else resolve(profile);
});
});
};...
...const promisifiedCallbackTwo = (profile) => {
return new Promise((resolve, reject) => {
client.createIdentitySearch(
...
,
(search, error) => {
if (error) reject(error);
else resolve(search);
}
);
});
};
...
...const profile = await promisifiedCallbackOne();
const search = await promiseFunction2(profile);
...
Thank you for reading.
I am running a discord channel for junior and mid level developers, if you are interested checkout https://discord.gg/3R9P2XU