Promises in JavaScript

Intro

Promises bring order to a world that used to be full of callbacks. To show you what I mean, take the following code for example:

arr.forEach(function(id){
    getData('./data/' + id, function(data, status){
        if(status === 4){
            handle(data);
        } else{
            logError('./errorlogger.aspx', 'Unable to get data', function(status){
                if(status !== 4){
                    alert('Something is wrong!');
                }
            })
        }
    })
});

This simplified example shows what can and does happen when callbacks are heavily used. Let's take a look and see how promises help.

Promises

First and foremost, you'll want to get somewhat acquainted with the underlying syntax.

Creating a new promise.


const promise = new Promise((resolve, reject)=>{
	if(/* desired state */) {
		resolve('Success')
	}
	else {
		reject('Failure')
	}
});

then/catch/finally

The most common methods used for a promise are then, catch, and finally.

promise.then((result)=> { 
	console.log(result)
})
promise.catch((error)=>{
	console.error(error)
})
promise.finally(()=>{ //does not have a parameter
    console.log('All done')
});

Example with a timeout

With the following example, I use a promise to log "Success" after 5000 ms.

const promise = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve("Success")
    }, 5000)
})
promise.then((result)=>{
    console.log(result) // Success
})

Timeouts are not the most common usage of promises. The most common usage of promises revolves around server calls with XMLHttpRequests, fetch, or one of several AJAX libraries.

I will be using fetch in the following example as it is available in most browsers without added libraries. fetch is promise based.


const promise = fetch('./data/1').then((result)=> result.json())  
promise.then((obj)=> console.log(obj))
promise.catch((err)=> console.log(err))

Promise API

The Promise object has methods that enable you in special use cases.

Promise.resolve & Promise.reject

You can directly return a resolved or rejected promise. Honestly, I've not had the need for these functions in production, but there may be a use case in testing.

Promise.resolve()
Promise.reject(err)

Promise.all

If you have multiple promises (promise1, promise2, promise3), and you want to wait for them all to be resolved, Promise.all is what you need.

const promise = Promise.all([ //passing an array
    promise1,
    promise2,
    promise3
])
promise.then((resultArray)=>{
    console.log('Success')
})

All of the promises need to be resolved to go onto .then.

Promise.allSettled (2019)

You may just want to know that all of the promises are completed (resolved or rejected). In 2019, the TC39 approved the allSettled method into the specification for this use case.

const promise = Promise.allSettled([ //passing an array
    promise1,
    promise2,
    promise3
])
promise.then((resultErrorArray)=>{
    console.log('Success or failure')
})

Promise.race

You may need to know when the first promise is complete. race is what you need.

const promise = Promise.race([ //passing an array
    promise1,
    promise2,
    promise3
])
promise.then((resultOrError)=>{
    console.log('Success or failure')
})

Promise.any (experimental)

const promise = Promise.any([ //passing an array
    promise1,
    promise2,
    promise3
])
promise.then((resultOrError)=>{
    console.log('Success or failure')
})

any is different from race in that it takes the first resolved result. Whereas, race takes the first settled result (resolved or rejected).

End

Promises are a foundation for the async/await API. Even without async/await, they provide a cleaner syntax than callbacks.

You may also like

  • Set array length to variable before loops

    Somebody may have told you at some point that you need to store array lengths to variables prior to starting a for loop.

    Read More >
  • Angular vs. Vue.js Reactivity

    VueJS is a relatively new library in this ecosystem of JavaScript frameworks that we have. AngularJS has, on the other hand, been long established and has a lot of backing. However, one developer's MV* library may prove to be an interesting alternative.

    Read More >