Async Operations in Javascript
Callbacks, promises, async/await
function myOperation(callback){
// do stuff
callback({message: 'hello'});
}
myOperation(function (data) {
console.log(data.message)
})
console.log('hello2')
1
2
3
-> hello2
-> hello
Callback problems
function myOperation(callback){
// do stuff
if(condition){
callback({message: 'result 1'});
}
if(otherCondition) {
callback({message: 'result 2'});
}
}
myOperation(function (data) {
console.log(data.message)
})
Can be called more than once
-> result 1
-> result 2
No guarantee of calling
function myOperation(callback){
// do stuff
if(condition){
// developer forgot to call callback here
} else {
callback({message: 'result'})
}
}
myOperation(function (data) {
console.log(data.message)
})
will never be executed
when "condition" is true
No guarantee of calling with parameters in the right order
function myOperation(callback){
// do stuff
if(condition){
callback(new Error('Oops. Something wrong happened'))
} else {
callback({message: 'result'})
}
}
myOperation(function (err, data) {
console.log(err)
})
condition == true ->
Error: Oops. Something wrong happened
condition == false ->
{message: "result"}
Callback Hell
function saveUser(callback){
// do stuff
callback({user: {id: 124}})
}
function sendRegistrationEmail(userId, callback){
// do stuff
callback({success: true})
}
function saveAnalytics(userId, callback){
// do stuff
callback({success: true})
}
saveUser(function (data) {
const userId = data.userId
sendRegistrationEmail(userId, function(result){
if(result.success){
saveAnalytics(userId, function(analyticsResult) {
if(analyticsResult.success){
// We finally completed our operations
}
})
}
})
})
Parallel callbacks
let results = [];
let completed = false;
operation1(function (result1){
results.push(result1)
if(completed){
onComplete()
}
})
operation2(function (result2){
results.push(result2)
if(completed){
onComplete()
}
})
function onComplete(){
console.log(results)
}
array with 2 results
Promises
A Promise can have 3 states
- pending: initial state, operation in progress
- fulfilled: operation completed successfully
- rejected: operation failed
A Promise is a utility built on top of callbacks which aims to fix callback related problems
function operation(){
return new Promise((resolve, reject) => {
resolve({message: 'operation result'})
})
}
operation()
.then((result) => {
console.log(result.message);
})
console.log('Hello')
Promise example
-> operation result
1
2
3
Promise anatomy
operation()
.then((result)=> {
})
.catch((err) => {
})
.finally(() => {
})
-> pending
-> fulfilled
-> rejected
-> either fulfilled or rejected
Promise chaining
operation()
.then((result)=> {
return operation2(result);
})
.then((result2) => {
})
.catch((err) => {
})
1
2
3
Parallel promises
function operation1(){
return new Promise((resolve, reject) => {
resolve({message: 'mesaj 1'})
})
}
function operation2(){
return new Promise((resolve, reject) => {
resolve({message: 'mesaj 2'})
})
}
Promise.all([operation1(), operation2()])
.then((results) => {
console.log(results)
})
Promise properties
function operation(){
return new Promise((resolve, reject) => {
resolve({message: 'message 1'})
resolve({message: 'message 2'})
})
}
Resolve or reject can be called only ONCE and has 0 or 1 parameters
Promise advantages
- more robust compared to callbacks
- makes sequential, parallel operations easier to write and understand
- enforces some rules which makes the code more robust
Async/await
Language utilities built on top of promises.
Supported in:
- Node.js > 7.6
- All browsers except Internet Explorer
function operation(){
return new Promise((resolve, reject)=>{
resolve({message: 'result'})
})
}
operation()
.then((result) => {
console.log(result.message)
})
console.log('New message')
async function operation(){
return {message: 'result'}
}
let result = await operation()
console.log(result.message)
console.log('New message')
1
2
3
1
2
3
Async/await example
Async/await code execution is different!
Be aware of this especially when trying to do parallel operations
Sequential operations
let user = await saveUser(userData);
let emailResult = await sendRegistrationEmail(user);
await saveAnalyticsData()
try {
let user = await saveUser(userData);
let emailResult = await sendRegistrationEmail(user);
await saveAnalyticsData()
} catch (err) {
// error handling is done here
} finally {
// always executed
}
Error handling
Parallel operations
try {
let results = await Promise.all([operation1(), operation2()])
} catch(err){
// error handling here
}
async/await advantages
- simpler code sequential looking code
- very easy to do sequential operations
- executes slightly different compared to promises
- can be confusing at the beginning
gotchas
Async Javascript Operations
By Cristi Jora
Async Javascript Operations
- 860