Advanced JavaScript
- Published on
Importance of JavaScript in Modern Web Development
JavaScript is a crucial component in modern web development. This scripting language has revolutionized the way people interact with web pages and has made it possible for developers to add a new layer to website interactivity. It has become one of the most popular programming languages over the years. With the advent of new technologies and frameworks, developers are now able to achieve sophisticated website design. In this article, I will explore the key concepts and core knowledge of JavaScript which is an important programming language in modern web development.
Advanced JavaScript Fundation 1
- Javascript Engine
- Javascript Runtime
- Interpreter / Compiler / JIT Compiler
- Writing Optimized Code
- Call Stack + Memory Heap
- Stack Overflow + Memory Leaks
- Garbage Collection
- Node.js
- Single Threaded Model
Advanced JavaScript Fundation 2
- Execution Context
- Lexical Environment
- Scope Chain
- Hoisting
- Function Invocation
- Function Scope vs Block Scope
- IIFE (Immediately Invoked Function Expression)
- this Keyword in Javascript
Definition
This -- is the object that the function is a property of
This -- Who call it (refers to whatever is on the left of the .(dot))
const obj = { property: `I'm a property of obj`, method: function(){ // this refers to the object obj console.log(this.property)'' } }; // I'm a property of obj // obj is to the left of the dot obj.method();
this 4 ways:
new keyword binding - the new keyword changes the meaning of this to be the object that is being created (in this example is person1)
// new binding function Person(name, age) { this.name = name this.age = age console.log(this) } const person1 = new Person('person1', 55) // this = Person { name: 'person1', age: 55 }
implicit binding - "this" refers to the object that is calling it. It is implied, without doing anything its just how the language works.
//implicit binding const person = { name: 'person', age: 20, hi() { console.log('hi ' + this) }, } person.hi() // this = person { name: 'person', age: 20, hi(){...} }
explicit binding - using the "bind" keyword to change the meaning of "this".
//explicit binding const person3 = { name: 'person3', age: 50, hi: function () { console.log('hi ' + this.setTimeout) }.bind(window) } person3.hi() // hi function setTimeout() { [native code] }
arrow functions as methods - "this" is lexically scoped, refers to it's current surroundings and no further. However, if "this" is inside of a method's function, it falls out of scope and belongs to the window object. To correct this, you can use a higher order function to return an arrow function that calls "this".
// arrow functions inside objects const person4 = { name: 'person4', age: 40, hi: function () { var inner = () => { console.log(this) } return inner() }, } person4.hi() // this = person4 { name: 'person4', age: 40, hi() {...} } // if either function is changed around, it doesn't work
- Call Apply Bind in JavaScript
Call is a method of an object that can substitute a different object than the one it is written on.
const wizard = { name: 'Merlin', health: 100, heal(num1, num2) { return (this.health += num1 + num2) }, } const archer = { name: 'Robin Hood', health: 30, } console.log(archer) // health: 30 wizard.heal.call(archer, 50, 20) console.log(archer) // health: 100
In this example call is used to borrow the heal method from the wizard and is used on the archer (which is actually pointing this to archer), with the optional arguments added.
Apply is almost identical to call, except that instead of a comma separated list of arguments, it takes an array of arguments.
// instead of this // wizard.heal.call(archer, 50, 20) // apply looks like this wizard.heal.apply(archer, [50, 20]) // this has the same result
Bind Unlike call and apply, bind does not run the method it is used on, but rather returns a new function that can then be called later.
console.log(archer) // health: 30 const healArcher = wizard.heal.bind(archer, 50, 20) healArcher() console.log(archer) // health: 100
- Currying with bind
Currying is breaking down a function with multiple arguments into one or more functions that each accept a single argument.
function multiply(a, b) { return a * b } let multiplyByTwo = multiply.bind(this, 2) multiplyByTwo(4) // 8 let multiplyByTen = multiply.bind(this, 10) multiplyByTen(6) // 60
Prototypal inheritance of TikTok assessment
function Activity(amount) { this.amount = amount } Activity.prototype.setAmount = function (value) { if (value <= 0) { return false } else { this.amount = value return true } } Activity.prototype.getAmount = function () { return this.amount }
Why this can be accessed in Activity Prototype ?
In JavaScript, when you create a new object instance with the new keyword, the instance inherits from the prototype of its constructor. This is called prototype inheritance. Every JavaScript function (except the arrow function, which does not have this binding) has a prototype attribute, which is an object. When an object is created through this constructor, the new object will inherit the properties and squares of the prototype object. Law.
When you call new Activity (amount), the following things happen:
A new empty object was created.
Pointing the
__proto__
attribute of this new object to Activity.prototype means that it inherits all properties and methods on Activity.prototype.The Activity constructor calls the newly created object as the context (i.e. this points to the new object).
Inside the constructor, this.amount sets the amount property of the new object.
Therefore, when you create an instance of Activity through new Activity (amount), the instance has its own amount attribute. When you call the setAmount or getAmount method of the instance, because these methods are defined on Activity.prototype, the JavaScript engine will look along the prototype chain and find these methods from the current object instance. Within these methods, this keyword refers to the object instance that calls the method, that is, the object with the amount attribute.
This is why this.amount can be accessed in the method defined by Activity.prototype: every time the method is called by an object instance, this points to that instance, so you can access the amount property defined in the constructor.
Why can use Call() to fullfil protoype inheritance and How ?
function Payment(amount, receiver) { Activity.call(this, amount) // Inherit Activity this.receiver = receiver } Payment.prototype = Object.create(Activity.prototype) Payment.prototype.constructor = Payment Payment.prototype.setReceiver = function (receiver) { this.receiver = receiver } Payment.prototype.getReceiver = function () { return this.receiver }
In JavaScript, inheritance usually refers to the properties and methods of one object directly accessing another object. This mechanism is usually implemented through the prototype chain. However, in this example,
Activity.call(this, amount);
uses a different method to achieve similar inheritance effects. The call method here is a method of the Function object in JavaScript, which allows you to call a function while changing the direction of this inside the function body. WhenActivity.call(this, amount);
is executed,this
keyword in theActivity function
will be set to the currentPayment
instance. Activity here is likely to be a constructor that is used to initialize newly created objects. By using call, you actually pass the Payment instance to Activity as this, so that the activity constructor can set properties and methods for the Payment instance, as if the Payment instance is constructed through Activity. The function is created the same. This method is not a real prototype inheritance, because there is no activity on the prototype chain of Payment. However, it allows the Payment instance to reuse the logic defined in the Activity constructor.More Obvious - notice the comments in below code snippet
function Activity(amount) { this.amount = amount } Activity.prototype.perform = function () { // perform some activity } function Payment(amount, receiver) { Activity.call(this, amount) // Borrow constructor this.receiver = receiver } // Inherit prototype Payment.prototype = Object.create(Activity.prototype) // Set constructor back to Payment after overwriting prototype Payment.prototype.constructor = Payment Payment.prototype.process = function () { // process the payment }
In this example,
Payment.prototype = Object.create(Activity.prototype);
ensures that the Payment object can access the method on the Activity prototype (e.g. perform). At the same time, we also fixed thePayment.prototype.constructor
pointer toPayment
, because prototype replacement will overwrite the pointer of the constructor.
- Since ECMAScript 2015 (ES6), JavaScript has introduced the class and extends keywords, making the inherited syntax clearer and easier to understand. Using the syntax of ES6, the above code can be written as:
class Parent { constructor(name) { this.name = name } sayName() { console.log('Parent name:', this.name) } } class Child extends Parent { constructor(name, age) { super(name) // use parent's constructor(name) this.age = age } sayAge() { console.log('Age:', this.age) } } const child = new Child('Alice', 10) child.sayName() // Output: Parent name: Alice child.sayAge() // Output: Age: 10
- In this ES6 example, the Child class inherits from the Parent class through the extends keyword. Super(name) in the subclass; calls the constructor of the parent class and passes the necessary parameters.
Types in JavaScript
7 types in JavaScript
- number
- string
- boolean
- undefined
- null
- Symbol('mySymbol')
{}
prmitive VS non-primitive type
pass by value VS pass by reference
shallow copy
// obj is an object defined before let clone = Object.assign({}, obj) // or let clone2 = { ...obj }
deep clone
let superClone = JSON.parse(JSON.stringify(obj))
// better deep clone method const myDeepCopy = structuredClone(myOriginal)
type coercion
means: the language converting a certain type to another type
1 == '1' //true
false == ""
// turefalse == []
// turefalse == {}
// false"" == 0
// ture"" == []
// ture"" == {}
// false0 == []
// ture0 == {}
// false0 == null
// false- only have:
null == undefined // true
- only have:
Dynamic VS Static typing
Weakly VS Strongly typing
TypeScript
The 2 Pillars in JavaScript:
Higher Order Functions
- a function that can take a function as an argument
- or
- a function that returns another function
- What is achieved by HOF is the ability to tell the function what to do during invocation, so that more flexibility contained in HOF than ever before. (keep out code dry and flexible)
// Higher Order Function: multiplyBy const multiplyBy => (num1) => (num2) => num1*num2; multiplyBy(4)(6) // 24
Functions VS Objects
Scheme + Java
Function Constructor:
const four = new Function('return 4') four() //4
Functions are first class citizens in JavaScript
- function can be assigned to a variable
- function can be passed into arguments
- return a function as an value
define function out of a loop and can run function in a loop
it's good to have default parameters set on a function
function a(param = 6) { return param } a()
1. Closures
function() + lexical scope = closures
function a() { let a = 'grandpa' return function b() { let b = 'father' return function c() { let c = 'son' return `${a} > ${b} > ${c}` } } } a()()() // 'grandpa > father > son'
Lexical Scoping: It is a scope determination mechanism, in which the scope of a function is determined by the position declared by the function in the code, not by the location of the call. This means that the scope chain of the function when it is created will remain inside the function, no matter where the function is called.
- Fixed scope chain
- When a function is defined, its scope chain has been determined, including its own scope and the scope that defines its environment.
- Function access variables
- A function can access variables in the scope when it is defined, even if it is in a different scope when the function is called.
- Closures
- The lexical scope is the basis of the closure. Closure is a special function that can remember and access the scope of its definition, even if it is called in its external scope.
- Nested functions
- In JavaScript, other functions can be defined within the function. The internal function will have access to the scope of external functions, which is determined by the rules of the lexical scope.
- Fixed scope chain
What does this means:
lexical environment === [[scope]]
in JavaScriptIn JavaScript, the two concepts of
lexical environment
and[scope]]
are closely related, but they are usually not completely equivalent. Let's explain these two concepts in detail:
Lexical Environment
"Lexical Environment" is an internal concept in JavaScript that defines how variables and functions are accessed and how they are associated with their respective scopes. Each execution context has a lexical environment associated with it, which contains the following two components:
Environment Record: This is a structure that stores variables and function declarations. In the lexical environment, this record contains all local variables and declared functions.
A reference to the outer environment: This is a reference to the external lexical environment to implement the scope chain. If a variable cannot be found in the current lexical environment, the engine will look for an external lexical environment.
[[Scope]]
(scope)
[[Scope]]
is an internal property of a function object that is set when the function is created. This property contains references to the lexical environment when functions are created, which is why closures can access variables in the environment in which they are defined.
Differences and connections
When a function is created, its
[[Scope]]
property is set to the lexic environment at the time of creation. This means that the function "remembers" the scope chain when it was created.The lexic environment is a broader concept that applies not only to functions, but also to block-level scopes (such as if statements and code blocks in the for loop).
In a sense, you can think of the
[[Scope]]
of a function as a snapshot of its lexical environment, but the lexical environment, as a broader concept, applies to the execution context of any code.
Conclusion
- To sum up, although the two are closely related, they are not technically exactly the same.
[[Scope]]
is an internal property of a function object, which refers to the lexical environment when functions are created; and the lexical environment is a broader concept that describes how variables and functions are accessed.
function CallMeFT() { setTimeout(function () { console.log(callMe) }, 4000) const callMe = 'Hi! I am Fangtai' } CallMeFT() // 'Hi! I am Fangtai'
Closures Benefits
Memory Efficient
- Because using closure, the big creation of 7000 length array only being created once and it supports multiple times usage without create this heavyDuty data structure again.
const getHeavyDuty = heavyDuty2() // function hoisting, so it works getHeavyDuty(550) // 'somthing' getHeavyDuty(6000) // 'somthing' getHeavyDuty(50) // 'somthing' function heavyDuty2() { const heavyDutyArray = new Array(7000).fill('somthing') console.log('big creation') return function (index) { return heavyDutyArray[index] } }
Encapsulation
- In below code snippet, I encapsulate the launch function from outside world so that not people can access the launch function.
const makeNuclearButton = () => { let timeWithoutDestruction = 0 const timePass = () => timeWithoutDestruction++ const totalPeaceTime = () => timeWithoutDestruction const launch = () => { timeWithoutDestruction = -1 return 'BOOOOM!' } setInterval(timePass, 1000) return { totalPeaceTime: totalPeaceTime, } }
Exercise
const array = [1, 2, 3, 4] for (var i = 0; i < array.length; i++) { setTimeout(function () { console.log('I am at Index ' + i) }, 3000) } // Output: // I am at Index 4 // I am at Index 4 // I am at Index 4 // I am at Index 4
- How do I fix above code snippet to
I am at Index 1 2 3 4
as Output- using let to alter var in for loop condition
- using closure and var
const array = [1, 2, 3, 4] for (let i = 0; i < array.length; i++) { setTimeout(function () { console.log('I am at Index ' + array[i]) }, 3000) } // Or const array = [1, 2, 3, 4] for (var i = 0; i < array.length; i++) { (function(closureI){setTimeout(function () { console.log('I am at Index ' + array[closureI]) }, 3000)})(i) } // Output: // I am at Index 1 // I am at Index 2 // I am at Index 3 // I am at Index 4
- How do I fix above code snippet to
Conclusion
- Closures allow a function to access variables from an closing scope or outer scope environment even after it leaves the scope in which it was declared.
2. Prototypal Inheritance
- Almost all objects in Javascript pass down properties through a prototype chain. We call this chain, prototypal inheritance. The child of the object "inherits" properties from its parent. All objects in JavaScript are descended from the Object constructor unless deliberately created or altered to not do so. The objects inherit methods and properties from Object.prototype. The prototype property also has an accessor property called
__proto__
that creates a link between the current object and points to the object it was created from, the "prototype". __proto__
points to theprototype
up to chain__proto__
property acually lives inprototype
- To be specific,
prototype
is prototype object Only functions have the prototype property
- The above sentence mains that the thing contains prototype object is always a function
{}.prototype // undefined [].prototype // undefined 'string'.prototype // undefined Object.prototype // yes. it has String.prototype // yes. it has
- Excercise
Date.prototype.lastYear = function(){ return this.getFullYear() - 1 } new Date(2023-11-11).lastYear() // 2022
// overwrite the map method in Array Array.prototye.map = function(){ let arr = [] for (let i = 0; i < this.length; i++){ arr.push((this[i] + 'MapLogo')) } return arr } console.log([1,2,3].map()) // 1MapLogo, 2MapLogo, 3MapLogo
OOP and FP
Benefits of programming paradigms:
- Clear + Understandable
- Easy to Extend
- Easy to Maintain
- Memory Efficient
- DRY
Factory functions
Object.create()
- Object.create() is a method in JavaScript that creates a new object and sets the prototype of this new object to another object. This means that the newly created object will inherit the properties and methods of the prototype object, but it is an independent object, and its modification will not affect the object as the prototype.
const elfFunctions = { attack() { return 'attack with' + this.weapon } } function createElf(name, weapon) { let newElf = Object.create(elfFunctions) newElf.name = name newElf.weapon = weapon return newElf; } const david = createElf('David', 'Cannon') console.log(david.attack()) // attack with cannon
Constructor Functions
- need to use
new
keyword in Javascript- the
new
keyword automatically returns the object - the
new
keyword also creates the elf constructor - any functions that is invoked using the
new
keyword is called a constructor function - constructor functions example:
Number()
Object()
- using the Capital letter to identify the constructor function
- the
function Elf(name, weapon) { this.name = name this.weapon = weapon } const david = new Elf('David', 'Cannon')
- How to create constructor function Natively in JavaScript
const Efl1 = new Function('name', 'weapon', `this.name = name; this.weapon = weapon;`) const daivd = new Efl1('David', 'Cannon') david // {name: 'David', weapon: 'Cannon'}
- Every function in JavaScript automatically gets a
prototype
property - About
new
keyword- return a object with
this
being re-pointed to callable object instead of global object (arrow function not included) - build a constructor in the function
- utilize
prototype
property to create methods that can be used in a callable object (only for constructor function) function Elf(name, weapon) { this.name = name this.weapon = weapon } Elf.prototype.attack = function() { return 'attack with' + this.weapon } const david = new Elf('David', 'Cannon') console.log(david.attack())
- return a object with
- about
this
in contextfunction Elf(name, weapon) { this.name = name this.weapon = weapon } Elf.prototype.attack = function() { return 'attack with' + this.weapon } Elf.prototype.build = function() { function building(){ return this.name + 'builds a house' } return building() // undefined } const david = new Elf('David', 'Cannon') console.log(david.attack())
- Becasue there is no object call building() inside of build function. To solve it:
- bind()
return building.build(this)
- variable store
const self = this
and then, usingreturn self.name + 'builds a house'
- arrow function
const building = () => {return this.name + 'bulds a house'}
- bind()
- Primitive values internally in JavaScript are transferred to object
var a = 5 a.toString() // '5'
- need to use
ES6 Classes
class Elf { constructor(name, weapon){ this.name = name this.weapon = weapon } // Within the class, methods (including constructors and other methods) // can be directly pasted without comma separation. attack(){ return 'attack with' + this.weapon } }
- class: a blueprint of creating an instance
- instantiation: using
new
keyword to create an instance through blueprint - pseudo classical inheritance (internally JS still uses prototype inheritance)
Inheritance
// Define a base class class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a noise.`); } } // Define a subclass that extends the base class class Dog extends Animal { constructor(name, breed) { super(name); // Call the parent class constructor with the name parameter this.breed = breed; } bark() { console.log(`${this.name} barks.`); } } // Create an instance of the Dog class, which is a subclass of Animal const myDog = new Dog('Rex', 'German Shepherd'); // Call methods inherited from the parent class and defined in the subclass myDog.speak(); // Output: Rex makes a noise. myDog.bark(); // Output: Rex barks.
- In this example, Animal is a base class with a constructor and a speak method. The Dog class extends Animal and adds a bark method. It also introduces a new property breed. Notice how the Dog class calls super(name) to pass the name to the constructor of the base class Animal. This is required to ensure that the Animal class is properly initialized before the Dog class adds its own properties and behavior.
Private Class Fields + Methods
class Employee { #name = "Test"; // private field setName(name) { this.#name = name; } } const emp = new Employee(); emp.#name = 'New'; // error emp.setName('New'); // ok
OOP in React
4 pillars of OOP
- Encapsulation
- Abstraction
- Inheritance
- polymorphism
Functional Programming
- side effect: function modifies the global variable outside of function itself
- pure functions:
- 1 task
- return statement
- pure
- no shared state
- immutable state
- composable
- predictable
- referential transparency: It is a concept of functional programming, which means that expressions can be replaced by their values without changing the behavior of the program
- idempotent: In computer science, "idempotent" is a feature of an operation, which means that the effect of performing the operation multiple times is the same as once. In other words, no matter how many times this operation is performed, the results are consistent
- Imperative vs Declarative
- Immutability: Not changing the data, not changing the state. Making a copy and return the new data
Higher order functions(HOF) and Closures
- closures' key point: the inner function which is inside of the outer function rememeber the variable used by or declared in the outer scope
- To some extent, closure can be used to achieve private data
Curry
- Currying is a technology that converts a multiparametric function into a series of functions that use one parameter. In other words, currying is a process of decomposing function parameters by partially applying parameters
const multiply = (a,b) => a*b const curriedMultiply = (a) => (b) => a*b const curriedMultiplyBy5 = curriedMultiply(5) curriedMultiplyBy5(4) // 20
Partial Application
const multiply = (a, b, c) => a*b*c const partialMultiplyBy5 = multiply.bind(null, 5) partialMultiplyBy5(4, 10) // 200
Memoization
- Caching: is a way to store values so you can use them later on
function memoizedAddTo80() { let cache = {} return function(n) { if (n in cache) { return cache[n]; } else { console.log('long time') cache[n] = n + 80 return cache[n] } } } const memoized = memoizedAddTo80() console.log('1', memoized(5)) console.log('2', memoized(5))
Compose and Pipe
- Composing or composition is the idea that any sort of data transformation that we do should be obvious (conveyor belt)
compose()
, JavaScript doesn't have compose natively so that developer need to use library- Ramda - A practical functional library for JavaScript programmers
- Build own compose function in JavaScript
- Compose: funcitions are executed from right to left, while Pipe: functions are executed from left to right
const mutiplyBy3AndAbsolute = compose(multiplyBy3, makePositive) const multiplyBy3 = (num) => num*3 const makePositive = (num) => Math.abs(num) const compose = (f, g) => (data) => {f(g(data))} const pipe = (f, g) => (data) => {g(f(data))} multiplyBy3AndAbsolute(-50) // 150
Arity
- simply means the number of arguments a function takes
Object.assign(arg1, arg2, arg3)
- arg1: new object
- arg2: thing that want to assign to arg1
- arg3: update
Amazon shopping solution (composable functional programming)
...args
this is rest parameter syntax to create an array of what input in it// Amazon shopping const user = { name: 'David', active: true, cart: [], purchases: [] } // 1st ...args is an array [{}, {}] // 2nd ...args is {}, {} const compose = (f, g) => (...args) => f(g(...args)) // main purchaseItem( emptyCart, buyItem, applyTaxToItems, addItemToCart )(user, {name: 'laptop', price: 200}) function purchaseItem(...fns) { return fns.reduce(compose) } function addItemToCart(user, item){ const updateCart = user.cart.concat(item) return Object.assign({}, user, { cart: updateCart }) } function applyTaxToItems(user) { return user } function buyItem(user) { return user } function emptyCart(user) { return user }
- Note that the definition order of the reduce function is from left to right, but the execution order is from right to left. This means that the last function in the array of functions will be executed first!
OOP VS FP
- OOP: few operations on common data, stateful, side effect, imperative
- Functional Programming: many operations on fixed data, stateless, pure, declarative
Asynchronous JavaScript
Asynchronous Functions are the functions can be executed later
How does JavaScript works? (explain the difference between Asynchronous and Synchronous)
- What is a program: allocate memory & parse and execute
- The V8 engine consists of 2 parts: memory heap & call stack
- Web APIs: DOM, AJAX, Timeout
- Callback Queue: execute the function that in Web APIs
- Event Loop: check if call stack is empty. If call stack is empty, check callback queue
Promise
A promise is an object that may produce a single value some time in the future (resolved value or rejected value)
A promise may be in one of 3 possible states: fulfilled, rejeceted, or pending
Callback function
const promise2 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'HI'); }); promise2.then((value) => { console.log(value); // will print 'HI' });
Here, setTimeout is set to call the resolve function after 100 milliseconds and pass the string 'HI' as a parameter. This means that promise2 will be resolved (or "cashed") after 100 milliseconds with the value 'HI'
In JavaScript, resolve and reject are two functions related to the Promise object, which are used to change the state of the Promise object.
const myPromise = new Promise((resolve, reject) => { // The code for asynchronous operation is executed here. const operationWasSuccessful = true; // Suppose this is based on the result of asynchronous operation. if (operationWasSuccessful) { resolve('Operation succeeded'); // Solve Promise and deliver successful results } else { reject('Operation failed'); // Reject Promise and pass the reason for the error or failure } }); myPromise .then((result) => { console.log(result); // If Promise is resolved, the code here will be executed. }) .catch((error) => { console.error(error); // If Promise is rejected, the code here will be executed. });
About
Promise.all()
Promise.all(urls.map(url => { return fetch(url).then(resp => resp.json()) })).then(results => { console.log(results[0]) console.log(results[1]) console.log(results[2]) }).catch(()=>cosole.log('error'))
Async Await
- underneath the hood, an async function is a function that returns a promise. But the benefit of Async / Await is that it makes code easier to read.
- The effect of barrier
async function asyncProcess() { const result1 = await doAsyncTask1(); //The first asynchronous task, waiting to be completed console.log(result1); // This line will wait for doAsyncTask1() to resolve before execution const result2 = await doAsyncTask2(); console.log(result2); const result3 = await doAsyncTask3(); console.log(result3); // more... } // Hypothetical asynchronous function, return Promise function doAsyncTask1() { return new Promise(resolve => setTimeout(() => resolve('Task 1 completed'), 1000)); } function doAsyncTask2() { return new Promise(resolve => setTimeout(() => resolve('Task 2 completed'), 1000)); } function doAsyncTask3() { return new Promise(resolve => setTimeout(() => resolve('Task 3 completed'), 1000)); } // Call the asynchronous processing function asyncProcess();
ES9 (2018)
- Object spread operator
const animals = { tiger: 23, lion: 5, monkey: 2, bird: 40 } function spreadOperator(p1, p2, p3){ console.log(p1) console.log(p2) console.log(p3) } const {tiger, lion, ...rest} = animals spreadOperator(tiger, lion, rest) //23, 5 { monkey: 2, bird: 40 }
- promise:
.finally()
which means no matter.then()
or.catch()
is called,finally()
will be called - for await of feature
// urls has been defined with links array const getData2 = async function() { // because fetch with url return promise const arrayOfPromises = urls.map(url => fetch(url)) for await (let request of arrayOfPromises) { // Block it. Make sure that the data in each loop can be obtained. const data = await request.json(); console.log(data) } }
Job Queue (Microtask Queue)
- where promises are located and has a higher priority than callback queue
Parallel, Sequence and Race
- parallel:
Promise.all(promises)
- race:
Promise.race(promises)
- sequence:
Async / Await 1 Await 2 Await 3
- parallel:
ES2020
Promise.allSettled()
this will give the status likefulfilled
for resovling andrejected
for rejectingPromise.all()
has to have all promises resolve and Not throw any error
Threads, Concurrency and Parallelism
Modules in JavaScript
arr.at(-1)
at function of an array provide an access for getting the data from not only positive index but negative index- Top level Await (used in Module)
Error Handling
Hi there! Want to support my work?