ES6 Promises
At first there were callbacks...
function doSomething(callback) {
let value = 42;
// some more code ....
callback(value);
}
doSomething(function(value) {
console.log('Got a value:' + value);
});
And events ...
let img1 = document.querySelector('.img-1');
img1.addEventListener('load', function() {
// woo yey image loaded
});
img1.addEventListener('error', function() {
// argh everything's broken
});
What happens if we hook the event handler after the event already happened?
So what is a promise?
A Promise is a container for an asynchronously delivered value. It's like a special box that preforms a task, this box is promised to contain the result sometime in the future
let p = new Promise(function(resolve, reject) {
if (/* condition */) {
resolve(/* value */); // fulfilled successfully
}
else {
reject(/* reason */); // error, rejected
}
});
Getting results
// preform a task (often async task)
let p = new Promise((resolve, reject) => {
// typeof(resolve) === "function"
// typeof(reject) === "function"
// when task completes, call resolve()
setTimeout(() => resolve("some value"), 2000);
// if task fails, call reject or throw an error
});
// get the result
p.then(res => console.log(res)) // "some value"
// NOTE: then doesn't run the task, only gets the value
p.then(res => console.log(res)) // still "some value"
Demo!
Catching errors
// preform a task (often async task)
let p = new Promise((resolve, reject) => {
// when task completes, call resolve()
setTimeout(() => resolve("some value"), 2000);
// if task fails, call reject or throw an error
});
// get the result
p.then(res => console.log(res)) // "some value"
p.catch(err => console.log(err)) // catch an error
// or
p
.then(res => console.log(res))
.catch(err => console.log(err))
Anatomy of a promise
func(resolve, reject) {
...
}
resolved value
rejected value
.then()
.catch()
fulfilled (resolve was called)
rejected
(reject was called or error was thrown)
Promise states
- Pending - still working to the promise func
- Fulfilled (resolved)
- Rejected (or error was thrown)
Calling then - sync or async ?
- If the promise is fulfilled - we get the result synchronously
- If the promise is pending - our callback will be called asynchronously when the promise is resolved
- if the promise is rejected - our callback won't be called
What errors can we catch() ?
- A call to reject() inside the promise
- Thrown errors inside the promise
- Thrown errors inside a then()
let p = new Promise(...);
p.then((res) => {
console.log(res)
})
.catch(err => {
console.log(err)
})
Demo
Passing a promise
You can pass the promise around, as a parameter or return it in a synchronous fashion, just like every other value
function saveData(data) {
return new Promise((resolve, reject) => {
// do the work when finished, call resolve
// if an error occured, call reject
})
}
// use it like any other value, get notified when the promise is fulfiled
saveData(someDataObject).then((res) => {
console.log(res)
})
Consuming a promise
let p = new Promise((resolve, reject) => { ... });
p.then(val => console.log("fulfilled:", val))
.catch(err => console.log("rejected:", err));
// piping
p.then(val => val * 2)
.then(val => console.log(val)) // returned value of then is rewraped as a promise
.then(val => throw new Error()) // throwing an error in "then" will be handled by the catch
.catch(err => console.log("rejected:", err));
Using fetch()
// fetch returns a promise
fetch('some url')
.then(res => {
if(res.ok) {
return res.json(); // returned value is rewraped as a promise for the next then()
}
// this will be caught by the catch() func
throw new Error('Network response was not ok.');
})
.then(json => {
console.log(json)
})
.catch(err => {
console.log('There has been a problem with your fetch operation: ' + err.message);
});
Promise.resolve()
function getData(offline) {
if (offline) {
return Promise.resolve([])
}
else {
// ES6 fetch returns a promise natively
return fetch("https://jsonplaceholder.typicode.com/posts")
}
}
// consumed as a promise even if nothing async ever happens
getData(false)
.then(data => console.log(data)
.catch(err => console.log(err))
// same for Promise.reject(reason)
Promise.all([])
let itemUrls = [
'https://jsonplaceholder.typicode.com/posts',
'https://jsonplaceholder.typicode.com/users'
];
itemPromises = itemUrls.map(url => fetch(url));
Promise.all(itemPromises)
.then(promises =>
// we only get here if ALL promises fulfill
promises.forEach(fetchPromise => {
fetchPromise.json().then(data => console.log(data))
})
)
Nesting Promise.all
let urls = [...]
let itemPromises = urls.map(url => fetch(url));
Promise.all(itemPromises)
.then(results => {
Promise.all(results.map(res => res.json())).then(data => console.log(data))
})
Promise.race([])
let p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
let p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(value => {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
Promise methods
Promise.resolve(value)
Promise.reject(reason)
Promise.all(iterable)
Promise.race(iterable)
Promise.prototype.then(callback)
Promise.prototype.catch(callback)
Summary
Promises give us the ability to write asynchronous code in a synchronous fashion, with flat indentation and a single exception channel.
ES6 Promises
By Gabi Mor
ES6 Promises
- 608