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
setTimeoutcallbacks don't run immediately.>
const array = [];setTimeout(() => { array.push('it worked'); }, 1000);array;Result:
Promises'
thencallbacks are the same. Callbacks attached withthendon'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.resolvecall.)>
// 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:
[]
With
setTimeout, we also saw examples where we waited for some time to pass. That let us see thesetTimeoutcallback's effects.>
const array = [];setTimeout(() => { array.push('it worked'); }, 1000);array;Async Result:
['it worked']
Again, the same is true for promises. If we wait for the JavaScript engine to run the
thencallback, we'll see'it worked'show up in the array.>
const array = [];Promise.resolve('it worked').then(message => {array.push(message);});array;Async Result:
['it worked']
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 itsthencallbacks are still called, so ourarray.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 theconsole.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');async console outputBoth
'before'and'after'came out before thethens ran, which tells us that our code ran from top to bottom before any of thethencallbacks were called. This matches what we saw in an earlier lesson onsetTimeout.(Promise code and
setTimeoutcode 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;Async Result:
{fulfilled: ['before', 'after', 'then1', 'then2']}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 finalarray.pushcall in a promise chained withthen:promise2.then(...). Make sure that your newthencallback 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;});Async Result:
- Goal:
{fulfilled: ['before', 'then', 'after']}- Yours:
{fulfilled: ['before', 'then', 'after']}
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
thencallbacks. 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.