After a bit more research I choose to use async/await because this allowed me to write a sequence of functions that look very much like a regular synchronous calls.
Promises are fine in theory until you try to use them in real cases. Everyone tells you how nice and simple promises look but they don’t only show simple examples. Once you add the error checking and data manipulation that has to go in between the API calls, you end up with completely unreadable code.
With aysnc/away, you can write one general use function:
const asyncRequest = (method, url, headers, body={}) => {
return $request[method](url, { body: JSON.stringify(body), headers: headers } )
}
Then I can call this multiple times as in following (w/o the error checking):
createOrUpdateFDContact: async function (payload) {
url=fd_base_url + "/companies/autocomplete?name=" + payload.data.map_fields.company_name
res = await asyncRequest("get", url, fd_headers, body={})
// Get comany ID from above and append to JSON
url=fd_base_url + "/contacts"
res = await asyncRequest("post", url, fd_headers, body=record)
// Check if record added. If not and "duplicate" record error, then get contact id and call following:
res = await asyncRequest("put", url, fd_headers, body=record)
}
So as long as I’m inside createOrUpdateFDContact2, I can execute any number of API calls synchronously.
Below is more like a real world example of this. I am confident it would be unreadable/unmaintainable if implemented with the nested Promise reject/resolve/catch structure.
createOrUpdateFDContact2: async function (payload) {
initialize_globals(payload) // Gets API key and Domain from iparams
record=payload.data.record // JSON already in FD Contact structure
// Check to see if the company exists already
try {
url=fd_base_url + "/companies/autocomplete?name=" + payload.data.map_fields.company_name
res = await asyncRequest("get", url, fd_headers, body={})
}
catch(err) {
console.log("Error getting companies: ", err)
}
// Parse list of companies to find if we link company or create new
try {
// Add company to either company or source_company based on result above
companies=JSON.parse(res.response).companies
if(companies.length==0) {
// No matching company. Add company name to custom_fields
record.custom_fields.source_company=payload.data.map_fields.company_name
} else { // loop thru all to find exact match
for(let cname of companies) {
if(cname==payload.data.map_fields.company_name) record.companies_id=companies.id
}
}
// TODO: What if none match?
}
catch(err) {
console.log(err)
}
// Try to create contact.
// If fail then user most likely already exists and we need to update instead
try {
url=fd_base_url + "/contacts"
res = await asyncRequest("post", url, fd_headers, body=record)
}
catch(err) { // err is actually API response object containing list of errors
// Loop thru errors and look for code "duplicate_value". If so, call update on additional_info.user_id
fd_errors=JSON.parse(err.response).errors
dup=false
for(let fd_err of fd_errors) {
if(fd_err.code=="duplicate_value") dup=true
}
// If Add Contact resulted in duplicate_value, then try to update contact
if(dup) {
try {
// Update contact with additional_info.user_id
url=fd_base_url + "/contacts/" + errors[0].additional_info.user_id
res = await asyncRequest("put", url, fd_headers, body=record)
if(res.status!=200) {
// If updating record fails, then show error from add API call as well as output from this call
console.log("Add Contact API Error: ", err)
console.log("Update Contact API Result: ", res)
}
}
catch(err) {
console.log(err)
}
}
}
},
its obvious to many but for newbies like me, I’ll just explain that the key to making the API calls syncronous is the “await” keyword in front of the call to asyncRequest. Otherwise you get a promise back and not a result.
Some notes:
createOrUpdateFDContactis invoked from the events callback handler list:
events: [
{ event: 'onAppInstall', callback: 'onAppInstallHandler' },
{ event: 'onExternalEvent', callback: 'createOrUpdateFDContact' },
...
],
Also, if you are trying to understand stuff, put a console.log(res) to show the result of each API call. It will make things a lot more clear.