Skip to content

Instantly share code, notes, and snippets.

@gnapse
Last active July 8, 2020 15:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gnapse/3e20c2364bc3113ff314d38e8283c157 to your computer and use it in GitHub Desktop.
Save gnapse/3e20c2364bc3113ff314d38e8283c157 to your computer and use it in GitHub Desktop.
Promise vs async/await

then(onSuccess).catch(onFailure)

This one catches errors not only during the ajax request, but during the onSuccess handler execution.

const response = ajax(options).then(onSuccess).catch(onFailure)

The appropriate translation to async/await could be something like this:

try {
  const response = await ajax(options)
  onSuccess(response)
} catch(error) {
  onFailure(error)
}

Simple and fair enough. So far so good.

then(onSuccess, onFailure)

However, we usually do not want to catch errors occurring inside the onSuccess handler. This can lead to errors difficult to catch or even test for.

Imagine you have a bug inside your onSuccess handler. Something that throws an error under a certain condition. If your onFailure handler catches it too, in addition to catching legitimate errors during the ajax request, you may end up with "failed" network requests that did not actually fail.

That's why the original promise callback handler .then supports receiving two callbacks.

const response = ajax(options).then(onSuccess, onFailure)

Translating this one is, in my opinion, not doable in async/await clearly.

let response = undefined

try {
  response = await ajax(options)
} catch(error) {
  onFailure(error)
}

if (response !== undefined) {
  onSuccess(response)
}

Am I missing something here? Can the above be written more clearly? I admit that my promise-accustomed brain may be slipping though.

@resynth1943
Copy link

What if, when you use .then, the function throws an Error (using the throw operator)? This is, of course, mitigated by making ajax an async function.

@gnapse
Copy link
Author

gnapse commented Jun 14, 2019

@resynth1943 if you mean that in processing the results we knowingly want to throw an error (say the network request was ok, but its content indicates that the operation failed) then I would do this:

function catchResponseErrorCode(json) {
  if (json.error_code != null) {
    throw new Error(json.error_code); //intentional error case
  }
  return json; // success case
}

ajax(options)
  .then(catchErrorResponseCode)
  .then(onSuccess, onError) // <- your actual success and error handlers

If you wanted to do this with async/await, and still achieve that your onError handler does not end up catching unwanted errors occurring inside onSuccess (i.e. bugs in the onSuccess code) can you tell me how you'd handle this not using .then and using async/await?

@jostschmithals
Copy link

jostschmithals commented Jul 3, 2020

Interesting question!

I'd say that errors which occur in onSuccess should be caught directly in this function (if you really don't want to handle those errors, leave the catch block empty):

try {
  const response = await ajax(options); 
  onSuccess(response);
} catch(error) {
  onFailure(error);
}

const onSuccess = response => {
  try { 
    // ...
  } catch(err) {}
}

This leads to an equivalent solution with a nested try/catch:

try {
  const response = await ajax(options);
  try {      
    onSuccess(response);
  } catch(err) {}
} catch(error) {
  onFailure(error);
} 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment