Execute Program

JavaScript Concurrency: Promises Are Asynchronous

Welcome to the Promises Are Asynchronous lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • In a past lesson, we saw that setTimeout callbacks don't run immediately.

  • >
    const array = [];
    setTimeout(() => { array.push('it worked'); }, 1000);
    array;
    Result:
  • Promises' then callbacks are the same. Callbacks attached with then don't run immediately; they're only scheduled to run later.

  • (Note that the next code example says "result:", not "async result:", so it won't wait for the promise to run. We get the array as it existed immediately after the Promise.resolve call.)

  • >
    // Note that this code example doesn't wait for the promise to run!
    const array = [];
    Promise.resolve('it worked').then(message => {
    array.push(message);
    });
    array;
    Result:
    []Pass Icon
  • With setTimeout, we also saw examples where we waited for some time to pass. That let us see the setTimeout callback's effects.

  • >
    const array = [];
    setTimeout(() => { array.push('it worked'); }, 1000);
    array;
    Asynchronous Icon Async Result:
    ['it worked']Pass Icon
  • Again, the same is true for promises. If we wait for the JavaScript engine to run the then callback, we'll see 'it worked' show up in the array.

  • >
    const array = [];
    Promise.resolve('it worked').then(message => {
    array.push(message);
    });
    array;
    Asynchronous Icon Async Result:
    ['it worked']Pass Icon
  • When our code examples involve promises, we'll normally return the promise in the example's final line of code, like {fulfilled: 5} This time, we didn't do that. The promise still runs, and its then callbacks are still called, so our array.push(message) is still called.

  • Note that the two promise examples above are identical to each other, and the two timer examples are also identical to each other. The only difference is whether we look at the example's result immediately or wait for a while before looking at it. If we wait, we'll see the promise or timer's effect.

  • We can use some console.logs to see the order of operations in more detail. But there are no timeouts involved (yet) so the console.logs come out very quickly.

  • (In a future lesson we'll see a way to delay promises by using a regular setTimeout.)

  • >
    console.log('before');

    Promise.resolve('some value')
    .then(() => {
    console.log('then1');
    })
    .then(() => {
    console.log('then2');
    });

    console.log('after');
    Asynchronous Icon async console output
  • Both 'before' and 'after' came out before the thens ran, which tells us that our code ran from top to bottom before any of the then callbacks were called. This matches what we saw in an earlier lesson on setTimeout.

  • (Promise code and setTimeout code look very different. But their underlying scheduling and execution are quite similar!)

  • Here's the above example repeated, but with manual array-based instrumentation rather than console output. (Remember that in Execute Program fulfilled promise objects look like {fulfilled: theValueHere}.)

  • >
    const array = [];
    array.push('before');

    const promise = Promise.resolve('some value')
    .then(() => {
    array.push('then1');
    })
    .then(() => {
    array.push('then2');
    })
    .then(() => {
    return array;
    });

    array.push('after');
    promise;
    Asynchronous Icon Async Result:
    {fulfilled: ['before', 'after', 'then1', 'then2']}Pass Icon
  • Here's a code problem:

    Modify this code to ensure that 'after' is only pushed into the array after 'then' is pushed. You can do that by putting the final array.push call in a promise chained with then: promise2.then(...). Make sure that your new then callback returns the array so our test code can compare it against the expected value.

    const array = [];
    array.push('before');

    const promise1 = Promise.resolve('this value is ignored');
    const promise2 = promise1.then(() => {
    array.push('then');
    });

    promise2.then(() => {
    array.push('after');
    return array;
    });
    Asynchronous Icon Async Result:
    Goal:
    {fulfilled: ['before', 'then', 'after']}
    Yours:
    {fulfilled: ['before', 'then', 'after']}Pass Icon
  • We've now seen the core idea in promises: they schedule code to run later, and we can add callback functions that will run after a promise fulfills. This shows us why promises are called promises: when we create one, we're promising to provide a value at some point in the future.

  • Finally, two quick notes about style. First, many of our examples instrument promises by creating an array outside the promise, then modifying it from inside a promise's then callbacks. In most application code, that would be bad style: promise callbacks usually shouldn't reach "outside" themselves to modify arrays or other data. But in this course, we often want to observe promises' effects "from the outside". Modifying a global array is an easy way to do that.

  • Second, you may notice that our formatting of promises is inconsistent. Sometimes our .thens are on the same line as the promise; sometimes they're not. Sometimes their indentation matches the line above; sometimes it doesn't. All these formatting styles are used in real-world code. This course intentionally mixes styles to expose you to code you might encounter.