Before you start reading this advanced part, We have discussed some of ES6 features:
Do you know ES6 - Part 1
Do you know ES6 - Part 2
Table Of Contents
IIFE
IIFE refers to Immediately Invoked Function Expression. IIFE is a JavaScript function that runs as soon as it is defined. MDN Web Docs
IIFE is differ from the traditional function which we can call it more than once but IIFE isn't. IIFE used only once. So we won't use it again. This means the variables in function can't be accessed so they are immutable.
One of benefits of IIFE is to create local scope and this is so important if I have many js files that may have the same variable names. So IIFE avoid overriding and protect the scope of its variables.
There two way to call the function:
- First, Our traditional way to define and call the function
function printName(){
let myName = "Mohamed"
console.log(myName)
}
//Invoke
printName()
- Second, Using IIFE. We wrap our function in brackets then we add a pair of brackets at the end of the function
(Our function)(Invoke)
(Our function)()
(function printName(){
let myName = "Mohamed"
console.log(myName)
})()
In fact we don't need to give the function a name because its called only once. So IIFE will usually be anonymous function
(function(){
let myName = "Mohamed"
console.log(myName)
})()
Closures
Closures is when a function remembers its lexical scope even when a function is executed outside the lexical scope. So closure is when a function use a variable defined in another function or another scope. So it make a link to this variable to update its value.
In the example, We have a printName function that have a variable. Then we have nested print function that use this variable in this scope. Then We have closure function that call the print function. Finally, We can call this function in another scope.
In other words, We can execute print function that use a name variable. This variable isn't declared here in closure function scope. But this variable is in printName function scope.
By default the logic is wrong. But in fact this is the closure and how it works. So if we change or update the value of our variable name, The closure will update it.
function printName(){
var name="Mohamed";
//name="Mohamed Khaled";
function print() {
console.log(name);
}
closure(print);
}
function closure(func)
{
func();
}
printName();
Another example, We can get and update variable x in the inner function
function outer(){
let x = 4
function inner (){
let y = x
y = 16
console.log(x)
console.log(x*2)
console.log(y)
}
closure(inner);
}
function closure(inn)
{
inn();
}
outer()
This is another way to create the previous closure function. Here's we have been replaced the inner function with anonymous function that return multiple values in an array. Then we executed the outer function.
function outer(){
let x = 4
return function (){
let y = x
y = 16
return [x,x*2,y]
}
}
//IIFE
console.log(outer()());
//let res = outer()
//console.log(res());
let's look at another example, It is a simple counter using closure. Anyway, I recommend you to use ++n and see the difference.
function counter(n){
return function (){
return n++
}
}
let res = counter(1)
console.log(res());
console.log(res());
console.log(res());
console.log(res());
console.log(res());
let's dive into more complex. What do you expect the output of this code? THINK!!
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},100);
}
After thinking. The output is the last value of our counter i. Which is 10.
Because i is a variable defined in the global scope. So this happened because of the closure. Again, Clousure use the last value of our variable that is defined in another scope.
I think you want to know how to solve this problem? Ok, There is more than one solution. One of them to use let to create the counter i because let is a local scope not global.
for(let i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},100);
}
We can solve it using IIFE function which is executed immediately. So closure fixing setTimeout.
function closure (index){
setTimeout(function(){
console.log(index)
},100)
}
for(var i=0;i<10;i++){
(closure)(i)
}
Synchronous vs Asynchronous
Synchronous programming
Synchronous programming means your code runs line by line, function by function. So you can't run two functions at the same time.
Asynchronous programming
Asynchronous function in three simple words means "it can wait". In another words means your function can be run while another function is running. So you can run two functions at the same time without freezing the program.
Asynchronous functions are coming from Web APIs that have a lot of asynchronous functions. JS has many of built in asynchronous function such as setTimeOut, setInterval, Promises, Event handlers and etc.
There's another type of function called Callback function which is executed after asynchronous function ends
In the next example, Weβre defining a function getStudent that take a callback function as a parameter. Then we are calling the callback function that return the name and the age of student with delaying the response for 2 seconds.
Finally we call getStudent and pass the callback function as a parameter and this function is invoked when the 2 second delay is passed.
From the output, The last console.log statement is executed first because the execution of the callback function is still delayed by 2 second, so the the output is delayed.
const getStudent = callback => {
setTimeout(() => {
callback ({ name: 'Mohamed', age: 23 })
}, 2000)
}
getStudent(student => {
console.log("This is executed second")
console.log(student.name, student.age)
})
console.log("This is executed first")
Promises
What is promise?
Promise is a built in asynchronous function in JS which handling asynchronous code easier.
A promise is an asynchronous action that may complete at some point and produce a value. So with Promise, We try to run some of operations. If the operations succeeded to run, we make for the promise something called resolve. If there a failure, we make reject. So promise deals with Asynchronous operations.
How to create promise?
We use a constructor called Promise that takes an executor function. This function tries to run the operations and make resolve or reject for the promise.
This is first promise
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('done')
}, 2000)
})
How to know if the promise works or not?
To know the result of the promise is resolve or reject. We use then and catch to get the result.
- Then takes a function runs successfully when occurs a resolve for promise. This means the action finished successfully. Also then returns another promise.
- Catch takes a function runs successfully when occurs reject for promise or it failed.
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('done')
//reject('Is not done. Error')
}, 2000)
})
p.then(() => console.log('promise resolved'))
.catch(() => console.log('promise rejected'))
The value for resolve or reject
Now, Whatever the result is resolve or reject. What if we need the value of this resolve or reject.
Here's our value for resolve is 'done' and our value fore reject is 'Is not done. Error'. So to get it, Our then or catch function takes a parameter.
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('Done')
reject('Is not done. Error')
}, 2000)
})
p.then((res) => console.log('promise resolved', res))
.catch((err) => console.log('promise rejected', err))
Nested promise
What if our promise ended and we want to run another promise. This is called nested promise.
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('Done')
}, 2000)
})
//Nested promise
p.then((res) => {
p.then(res2 => console.log(res2))
})
Chaining promise
I want to tell you that nested promise isn't good practice. So there chaining promise.
Here's our function return our promise p and the result of then function is our promise p. Finally we can use then to make chaining promise.
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('Done')
}, 2000)
})
//Chaining promise
p.then((res) => {
return p
}).then(res2 => console.log(res2))
//p.then((res) => p).then(res2 => console.log(res2))
Here's the final code
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('Done')
}, 2000)
})
//Nested promise
p.then((res) => {
p.then(res2 => console.log(res2))
})
//Chaining promise
p.then((res) => {
return p
}).then(res2 => console.log(res2))
//Chaining promise
p.then((res) => p).then(res2 => console.log(res2))
//Chaining promise .. Best practice and more readable
p
.then((res) => p)
.then(res2 => console.log(res2))
When I have chaining promise. If any promise is rejected, It will run the first catch and ignore the rest.
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
reject('Is not done. Error')
}, 2000)
})
//Chaining promise
p
.then((res) => p)
.then(res2 => console.log(res2))
.catch((err1) => console.log('promise rejected 1', err1))
.catch((err2) => console.log('promise rejected 2', err2))
Finally, Do you remember our callback example. I am going to make it with promise with the same output. Try to understand it LOL :)
Async vs Await
Async
Async is keyword, Await is operator. They have been added to ES8.
Async vs Await make us deal with promise in a way better than using promise chain so our promise became more easier.
Async used with a function that means this is asynchronous function but it return a promise.
- Return == Resolve
- Throw == Reject
In this example, Our promise resolve or return myName 'I am Mohamed'
async function myName(){
return 'I am Mohamed'
}
myName().then( msg => console.log(msg))
In this example, Our promise reject or throw isName 'Is not Mohamed'
async function isName(){
throw 'Is not Mohamed'
}
isName().catch( msg => console.log(msg))
await
await means you have to wait until you execute this line. await is only valid in async function.
In the next example, We have promise p and async function called myName. We will notice that 'This is executed first' is first line but then we have to wait until our promise p ends. Finally after the promise done, The rest is executed so last line is 'I am Mohamed'.
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('Done')
}, 2000)
})
async function myName(){
console.log('This is executed first')
await p
//p
console.log('I am Mohamed')
}
myName()
Another example
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('Done')
}, 2000)
})
async function myName(){
console.log('This is executed first')
await p
console.log('I am Mohamed')
setTimeout(() => {
console.log('Last line')
}, 5000)
console.log('I am Egyptian')
}
myName()
You know promise make resolve or reject. Now, The result of await is the result of resolve or reject.
If the promise make resolve
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
resolve('Done')
}, 2000)
})
async function myName(){
let result = await p
console.log('The result of await is : ' + result)
}
myName()
If the promise make reject, It automatic throw the error. So we have to avoid promise chain and use this way.
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise done')
//resolve('Done')
reject('error 404')
}, 2000)
})
async function myName(){
let result = await p
return result
}
myName()
.then( res => console.log('The result of await is : ' + res))
.catch( err => console.log('Error: ' + err))
Finally, Do you remember our callback example. We did it in two ways using callback and promise.
Now, I am going to make it using async and await with the same output. Try to understand it again on your own :) LOL :(
- Also we can use try and catch to handle error
let p = new Promise((resolve, reject) => {
setTimeout(() => {
let error = false;
if(error)
{
console.log("This is executed second, Done")
resolve({ name: 'Mohamed', age: 23 })
}
else
{
console.log("This is executed second, Error")
reject()
}
}, 2000)
})
const getStudent = () => {
return p
}
async function fetchStudent () {
try {
const student = await getStudent()
return student
} catch (error) {
console.log("Error")
}
}
fetchStudent()
.then(student => console.log(student.name + " " + student.age))
.catch(() => console.log("error 404"))
console.log("This is executed first")
Conclusion
Thank you for reading and I hope you have found valuable information here.
Here is the repo, You can find the source code and feel free to fork it.
Top comments (0)