Skip to content

Instantly share code, notes, and snippets.

@mathesond2
Created July 22, 2022 17:58
Show Gist options
  • Save mathesond2/70c75517c6e93a98260dc216972a1056 to your computer and use it in GitHub Desktop.
Save mathesond2/70c75517c6e93a98260dc216972a1056 to your computer and use it in GitHub Desktop.
old notes on JS promises

at its root, a promise is an object to which you attach callbacks. these callbacks happen once the 'value' property changes.

example with callback

function successCallback(result) {
  console.log('Audio file ready at URL: ' + result);
}
 
function failureCallback(error) {
  console.error('Error generating audio file: ' + error);
}
 createAudioFileAsync(audioSettings, successCallback, failureCallback);

example with promise

createAudioFileAsync(audioSettings).then(successCallback, failureCallback);

what makes a promise better than a callback?

  1. unlike passed-in callbacks (above), callbacks will never be called before the completion of the current run of the JS event loop. AKA the callbacks will wait their turn until the function/promise has resolved before firing.
  2. callbacks added with then() even after the success or failure of the async operation, will be called.
  3. multiple callbacks may be added by calling then() several times.

promise chaining

when we run .then() on a promise, the `then() function returns a new promise, different from the original.

ex:

const promise2 = doSomething().then(successCallback, failureCallback);

^ the second promise represents the completion not just of doSomething() but also successCallback or failureCallback you passed in, which can be other async fns returning a promise. when thats the case, any callbacks added to promise2 get queued behind the promise returned by either successCallback or `failureCallback.

chaining after a catch

you can chain after a failure aka a catch which is good to do stuff even after a promise chain has failed:

new Promise((resolve, reject) => {
  console.log('initial');
  resolve();
})
.then(() => {
  throw new Error('somethin failed');
  console.log('do this');
})
.catch(() => {
  console.error('do that (err)')
})
.then(() => {
  console.log('no matter what happened before, do this);
});
 
/*
Initial
Do that (err)
Do this, no matter what happened before
*/

error propagation.

in a standard callback pyramid of hell, you'd see failureCallback multiple times like:

doSomething(function(result) {
  doSomethingElse(
    result,
    function(newResult) {
      doThirdThing(
        newResult,
        function(finalResult) {
          console.log('Got the final result: ' + finalResult);
        },
        failureCallback
      );
    },
    failureCallback
  );
}, failureCallback);

though in a promise you only see it once? why?

doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => console.log(`Got the final result: ${finalResult}`))
  .catch(failureCallback);

so if there's a problem, the promise chain will be looking for a catch handler and handle it there. this is super related to regs synchronous code:

try {
  const result = syncDoSomething();
  const newResult = syncDoSomethingElse(result);
  const finalResult = syncDoThirdThing(newResult);
  console.log(`Got the final result: ${finalResult}`);
} catch (error) {
  failureCallback(error);
}

creating a promise around an old callback API

you can create a promise from scratch using its constructor. this should be only needed to wrap old APIs. ideally, all async fns would already return promises. but some APIs still expect sucess/failure callbacks the old school way.

ex: (remember sa)

setTimeout(() => saySomething('10 seconds passed'), 10 * 1000);

if something fucks up in saySomething(), nothing acknowledges or catches this. but we can wrap setTimeout in a promise.

const wait = ms => newPromise(resolve => setTimeout(resolve, ms));
 
wait(10 * 1000)
  .then(saySomething('10 secs passed'))
  .catch(failureCallback);

prime example of JS adding a promise .then() fn to the microtask queue which then checks via the event loop to make sure the callstack is empty:

Promise.resolve().then(() => console.log(2));
console.log(1);

promise.reject

promise.reject() returns a promise object that is rejected with reason for promise fail.

function resolved(result) {
 console.log('resolved');
}

function rejected(result) {
 console.err(result);
}

Promise.reject(new Error('fail')).then(resolved, rejected);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment