console.log('this prints first');
setTimeout(function() {
console.log('this prints last');
}, 0);
console.log('this prints second');
JavaScript code runs synchronously, but we have a way of handling async code: asynchronous callbacks.
This code does not run "out of order". However, the callback to setTimeout is not executed right away, it runs after a timer expires on something called the "event loop".
var planet;
$.getJSON('https://swapi.co/api/planets/1/', function(response) {
planet = response;
});
console.log(planet);
Let's try to pull some data with jQuery's AJAX request, which utilizes callbacks
What is the value of planet?
Why is it undefined?!
The console.log was synchronous; it ran before the asynchronous callback.
var planet;
$.getJSON('https://swapi.co/api/planets/1/', function(response) {
planet = response;
console.log('done', planet);
});
console.log('waiting');
Let's fix it!
Asynchronous callbacks run after the rest of the code
Once you are inside the callback, the code executes predictably as per usual, (unless there are more async callbacks in there)
Check out MDN's discussion of callbacks.
A Promise is a new global object (as of ES6) that serves as a placeholder for asynchronous operations.
fetch('https://httpbin.org/get');
// -> Promise {<pending>}
fetch is a modern web API built-in to the browser that uses promises.
Promises provide a .then and a .catch, which both accept callbacks.
fetch('https://httpbin.org/get')
.then(function(response) {
// the Promise resolved successfully
console.log(response);
})
.catch(function(error) {
// the Promise was rejected
console.log(error);
});
You can chain .then statements together by having more promises, but you only ever need 1 .catch method at the end.
fetch('https://pokeapi.co/api/v2/pokemon/1/')
.then(function(response) {
return response.json();
})
.then(function(pokemon1) {
console.log(`The first pokemon is ${pokemon1.name}`);
return fetch('https://pokeapi.co/api/v2/pokemon/2/');
})
.then(function(response) {
return response.json();
})
.then(function(pokemon2) {
console.log(`The second pokemon is ${pokemon2.name}`);
})
.catch(function(error) {
// something bad happened
console.log(error);
});
fetch('https://pokeapi.co/api/v2/pokemon/1/')
.then(response => response.json())
.then(pokemon1 => {
console.log(`The first pokemon is ${pokemon1.name}`);
return fetch('https://pokeapi.co/api/v2/pokemon/2/');
})
.then(response => response.json())
.then(pokemon2 => console.log(`The second pokemon is ${pokemon2.name}`))
.catch(error => console.log(error));
Same exact functionality as the previous slide
jQuery .ajax and .get and .getJSON methods support a Promise-like* interface as well:
$.getJSON('https://pokeapi.co/api/v2/pokemon/')
.then(pokemon => {
console.log(`Holy crap there are ${pokemon.count} pokemon now.`);
})
.catch(err => {
console.log(err);
});
They are promises implemented before there were standard JavaScript promises.
Let's make our own promise to see how they work:
function waitForSomeTime(ms) {
return new Promise(function(resolve, reject) {
try {
setTimeout(function() {
return resolve('Time is up!');
}, ms);
} catch (error) {
return reject(error);
}
});
}
waitForSomeTime(1000).then(res => console.log(res))
function myAsyncFunction() {
// return a new Promise
return new Promise((resolve, reject) => {
/*
DO ASYNC STUFF HERE
*/
// if it succeeds, call the resolve callback
resolve(/* success value*/);
// if it fails, call the reject callback
reject(/* fail value*/)
})
}
$.getJSON('https://pokeapi.co/api/v2/pokemon/1/', pokemon1 => {
console.log(`The first pokemon is ${pokemon1.name}`);
return $
.getJSON('https://pokeapi.co/api/v2/pokemon/2/', pokemon2 => {
console.log(`The second pokemon is ${pokemon2.name}`);
return $
.getJSON('https://pokeapi.co/api/v2/pokemon/3/', pokemon3 => {
console.log(`The third pokemon is ${pokemon3.name}`);
return $
.getJSON('https://pokeapi.co/api/v2/pokemon/4/', pokemon4 => {
console.log(`The fourth pokemon is ${pokemon4.name}`);
})
.fail(error => console.log(error));
})
.fail(error => console.log(error));
})
.fail(error => console.log(error));
}).fail(error => console.log(error));
$.getJSON('https://pokeapi.co/api/v2/pokemon/1/')
.then(pokemon1 => {
console.log(`The first pokemon is ${pokemon1.name}`);
return $.getJSON('https://pokeapi.co/api/v2/pokemon/2/');
})
.then(pokemon2 => {
console.log(`The second pokemon is ${pokemon2.name}`);
return $.getJSON('https://pokeapi.co/api/v2/pokemon/3/');
})
.then(pokemon3 => {
console.log(`The third pokemon is ${pokemon3.name}`);
return $.getJSON('https://pokeapi.co/api/v2/pokemon/4/');
})
.then(pokemon4 => {
console.log(`The fourth pokemon is ${pokemon4.name}`);
})
.catch(error => console.log(error));
let fourPokemonPromises = [];
for (let i = 1; i < 5; i++) {
fourPokemonPromises.push(
$.getJSON(`https://pokeapi.co/api/v2/pokemon/${i}/`)
);
}
Promise.all(fourPokemonPromises)
.then(pokemon => pokemon.forEach(p => console.log(p.name)))
.catch(err => console.log(err));
Promise.all accepts an array of promises and will resolve when all of them pass or reject when any of them fail
This effectively lets you make several AJAX calls simultaneously, which can greatly improve the speed of your app.
var fourPokemonRace = [];
for (let i = 1; i < 5; i++) {
fourPokemonRace.push($.getJSON(`https://pokeapi.co/api/v2/pokemon/${i}/`));
}
Promise.race(fourPokemonRace)
.then(pokemon => console.log(pokemon.name))
.catch(err => console.log(err));
Promise.race accepts an array of promises and resolves or rejects the first one to finish.
When might this be useful?
$.getJSON('https://swapi.co/api/planets/1/')
.then(res => {
let planet = res;
let filmsPlanetWasIn = planet.films;
return Promise.all(filmsPlanetWasIn.map(film => $.getJSON(film)));
})
.then(films => console.log(`Tattoine was in the following films: ${films}`))
.catch(err => console.log('Uh oh', err));
Here is a combination of what we've learned; making a request to get data used to make a handful of requests efficiently.