Trending February 2024 # How Does Generic Class Work In Scala? # Suggested March 2024 # Top 2 Popular

You are reading the article How Does Generic Class Work In Scala? updated in February 2024 on the website We hope that the information we have shared is helpful to you. If you find the content interesting and meaningful, please share it with your friends and continue to follow and support us for the latest updates. Suggested March 2024 How Does Generic Class Work In Scala?

Definition of Scala Generic

Scala Generic classes are different then we have generic classes in Java. Generic classes are those classes which takes the type of the variable as the parameter. We are not sure which type of variable would be we just specify it in square brackets []. The type would be verified by the compiler at runtime. Generic classes are mostly utilized for the collection in scala. We mostly use parameter name as A to define the generic class type but it is not mandatory we can use any character to define it.

Start Your Free Software Development Course


class class_name[A] { private varvaribale_name: List[A] }

In the syntax above we are using A as the type for the list we are defining. This A can contain any data type like Int, Float, String or any other user defined object as well. We can use any other character name as well in the place of A it is the standard that we follow.

class Demo[A] { private varmyList: List[A] = {20, 30, 40, 50) } How does Generic Class Work in Scala?

Collections are very useful when we have same type of data into our list or set. This helps us from typecasting of the elements as well which reduce the line of code. Not with generic we define this type into the [] brackets for example;

using collection :val list = new List[Int] : we are specifying the type at the time of creation only. Now take a look at generic in scala; using generic : class MyDemo[A] {} val list = new MyDemo[String] val list = new MyDemo[Double] val list = new MyDemo[Float] val list = new MyDemo[Student]

In the above case, we have created one class with a generic type specify it by using A in the square brackets[]. Now at the time of object creation for the list we are mentioning its type as String or it can be anything we want. If we follow the standard given by scala then the generic parameter should be of single character only. Also, we can have multiple parameter as well see below;

: class MyClass[Key1, Value1]: In this, we are passing two-parameter here. Also, we can use these parameters with traits in scala which will make them generic too. For syntax how to make them generic see below;

class Jasmin extends Flower

Suppose we have flower class, rose and jasmine are the child class of Flower here. We have different type of variance related to this.

Question is: If Rose extends Flower so does a list of Rose also extend a list of Flower here from above example?

1. Invariance

IF we chose to answer No for the question then we would require to create the list of Rose and Flower separately and in this case we would use Invariance for this.

class InvariantList[A] valinvariantAnimalList: InvariantList[Flower] = new InvariantList[Rose]

In this we can define them in scala.

2. Covariance

If we choose to answer yes for the above question then we should go for Covariance here. We can define this Covariance by using the PLUS(+) symbol in the scala. Like this: class CovariantList[+A]

This + simplify that it is a covariance class. val f: FLower = new Rose valfList: CovariantList[Flower] = new CovariantList[Rose]

In this example, we are assigning the instance of rose to its parent class because it’s a subclass for flower and creating list for them.

3. Contravariance

If we choose not to answer any of the above then we can go for Contravariance in scala. In this, we use a minus sign (-) to make use of it while working in code. Below is the syntax to use this type in scala:

class ContravariantList[-A] Examples of Scala Generic Classes 1. Single Parameter Generic Classes

In this type we pass only one character to make it Generic in scala. See example below for better understanding;


object Main extends App{ defaddValues[A](a: A, b: A)(implicit x: Numeric[A]): A =, b) println("Sum of the values are  ::::  ") println(addValues(100, 300)) }


2. Contravariance

To define and use this we use minus operator (-).


object Main extends App{ valic = new ICICI valsb = new SBI valBankType = new BankType } abstract class Bank [-T]{ defrate : Unit } class ICICI extends Bank[Int]{ override def rate: Unit = { println("icici called  ::") println(" sub type icici !!") } } class SBI extends Bank[Int]{ override def rate: Unit = { println("SBI called  ::") println("sub type SBI !!") } } class BankType{ defshow(x: Bank[Int]){ x.rate } }

3. Covarience

To define this type we use PLUS (+) operator.


object Main extends App{ valic = new ICICI valsb = new SBI valBankType = new BankType } abstract class Bank [+T]{ defrate : Unit } class ICICI extends Bank[Int]{ override def rate: Unit = { println("icici called  ::") println(" sub type icici !!") } } class SBI extends Bank[Int]{ override def rate: Unit = { println("SBI called  ::") println("sub type SBI !!") } } class BankType{ defshow(x: Bank[Int]){ x.rate } }



Generic are important to write efficient code for our application. It makes then code more readable, easily, clear, and also reduces the repetitive logic and line of code in our program. We have discussed its various types also according to the different case we have more better understanding. They are mostly use with collection in scala.

Recommended Articles

We hope that this EDUCBA information on “Scala Generic” was beneficial to you. You can view EDUCBA’s recommended articles for more information.

You're reading How Does Generic Class Work In Scala?

How Does Singleton Object Work In Scala?

Definition of Scala singleton

Web development, programming languages, Software testing & others


Here we use the object keyword to create our singleton object. Inside this, we can write our logic. Let’s take a look below;

object Demo { def m1(message: String){ println("singleton method called.") } }

In the above syntax, we are using the object keyword to define our singleton object. Also, we can use this m1 () method anywhere by importing this class package. In singleton classes, we can define our utility method which can be used anywhere in the code to reduce the redundant code.

How does Singleton Object Work in Scala?

Singleton object works in the same way like static keyword in java. Singleton object means only one instance of the class not multiple. In java, we can access the static method by the name of the class but here in scala we just need to import the class package containing the method and we can us it directly. Singleton objects are lazy created objects as they have exactly only one instance of the class. In scala at the root level, the class object is a singleton.

Like; package Demopck object Demo { def m1(message: String){ println("message is ::" +message) } } import Demopck.Demo.m1 class Employee(name: String, id: Int) class Test { val emp1 = new Employee("ABC XYZ", 001) val emp2 = new Employee("IOP YHG", 005) val emp3 = new Employee("TYH PPP", 006) val emp4 = new Employee("OIU DFR", 007) m1("called singleton object method.") }

In the above example what we are doing is we made one singleton object. Also, we make one employee class to test our changes. This m1() method can be used as a utility method in any class just by importing the changes. In the test class, we have import the Demo class and method m1() at the end of the code we are calling our method directly without any class reference. That means the object of the class got created at the beginning only and we are referring to the same object to refer the method.

We can call the singleton object method by two ways;

1. By directly referring the class name followed by the method name that we want to call. In both the ways we do not need to create the object for the class we just need to refer the method by the class name append before the method.

eg: class_name.Method_name()

2. By importing the class which contains the method

eg: import packagename.classname.methodname

Points to be remembered while working with the singleton Scala objects;

In scala language, the main method is always present inside a singleton object because the flow of the program initiates from there only.

Our singleton object would be accessible globally in our application.

In scala, we can extend traits and class inside singleton classes.

We can create an instance of the singleton class. As it gets created only once.

We cannot pass parameters inside the constructor of the singleton class.

No need to create the object of the singleton class to access their method because they can directly be accessed via class name only.

Examples of Scala Singleton

Following are the examples as given below:

Example #1


object Main extends App{ object Demo { def m1(message: String): Unit = println(s"INFO: $message") } val emp1 = new Employee("TABC", 001) val emp2 = new Employee("TTYUs",002) val emp3 = new Employee("TOPO", 003) Demo.m1("calling singleton method ") } class Employee(name: String, id: Int)


Example #2

In this, we are calculating the area of the rectangle.


object Main extends App{ var result = new Rectangle(); result.area() ; } class Rectangle { var l = 20; var h = 40; defarea() { var area = l * h; println("Length rectangle is:" + l); println("Height rectangle is:" + h); println("Area rectangle is :" + area); } }


Example #3

In this example, we are printing our message by calling the method from singleton class.

object Main extends App{ } object SingletonDemo { var message1 = "Hello to all from singleton"; var message2 = "Message to be priented"; defshow() { println("string one is  :: "+ message1); println("string second is  :: "+ message2); } }


Example #4

In this example, we are passing some parameters to test the input and output through singleton objects.


object Main extends App{ DrawDemo.test(1) DrawDemo.test(2) DrawDemo.test(10) } object DrawDemo { var draw1 = "This is one drwaing"; var draw2 = "This is second"; deftest(num: Int) { if(num == 1){ println("draw 1 message is  :: " + draw1) } if(num == 2){ println("draw 2 message is  :: " + draw1) } println("nothing match !!!!") } } }


Conclusion Recommended Articles

We hope that this EDUCBA information on “Scala Singleton” was beneficial to you. You can view EDUCBA’s recommended articles for more information.

How Does Alias Work In Postgresql?

Introduction to PostgreSQL Alias

In simple terms, ALIAS means temporarily giving another name to a table or a column. To give the temporary name for tables or columns, we generally use PostgreSQL Alias. The PostgreSQL Aliases are used to create a temporary column or table name. The existence of aliasing is limited to the PostgreSQL statement’s execution means the PostgreSQL aliases are used to rename a column or a table in a specific PostgreSQL query. Hence the actual table name or column name does not change in the database. We generally use the temporary names while performing self join on the table to create a temporary table.

Start Your Free Data Science Course

Hadoop, Data Science, Statistics & others


We can use PostgreSQL Aliases to create a temporary name for columns, tables, or expressions. Let’s understand the syntax of Alias as below :

SELECT column [AS] alias_name FROM table;

2. PostgreSQL Aliases for expression

SELECT expression [AS] alias_name FROM table;

3. PostgreSQL Aliases for table

SELECT column FROM table [AS] alias_name;


Column: The actual column name to which we want to specify an alias.

table_name: The actual table name to which we want to specify an alias.

expression: An expression to which we want to specify an alias.

AS: It is the Optional keyword. AS keyword will not affect the Alias in the PostgreSQL statement, even if it is defined or not. In PostgreSQL, defining AS keyword or not is a programmer’s choice.

alias_name: The temporary name for expressions, columns, or tables. The PostgreSQL Alias name can have spaces. But it is not a best practice to have an alias name with spaces in case of aliasing a table.

How does Alias Work in PostgreSQL?

The PostgreSQL Aliases are used to remove the ambiguity for self-joins. The self join means the same table is getting scanned multiple times to retrieve the data. The PostgreSQL Alias is used with the optional ‘alias’ keyword, but if provided, it hides the actual name of the columns or tables. If we have specified the Alias in the PostgreSQL statement, we need to define the column names, and the Alias defined, and its scope is limited to the same statement only.


Consider a statement ‘FROM MyTable AS MT’, which we are using with a SELECT statement; then it uses the ‘MT’ and not the ‘MyTable’.

Consider the following statements to understand how PostgreSQL works for long table names:

SELECT long_table_name.column FROM long_table_name;

We can use the Alias for the long table name as follows:

SELECT ltn.column FROM long_table_name ltn;

Here we have specified the alias ‘ltn’ for a table ‘long_table_name’.

Examples to Implement Alias in PostgreSQL

Let’s create a table of name ‘student’ and ‘teacher’ to understand the PostgreSQL alias examples in detail:

Example #1

Create a table of name ‘student.’


CREATE TABLE student( rollno int PRIMARY KEY, firstname VARCHAR (50) NOT NULL, lastname VARCHAR (50) NOT NULL, branch VARCHAR (50) NOT NULL, result boolean, joining_date DATE NOT NULL );

Now, insert some data into thestudent’ table.

INSERT INTO student (rollno, firstname, lastname, branch, result, joining_date) values ('101', 'Oliver','Jake', 'Civil', false, '06-01-2024'), ('102', 'Jack','Connor', 'Computer', false, '06-01-2024'), ('103', 'Harry','Callum', 'Civil', false, '06-01-2024'), ('104', 'Jacob','John', 'Computer', false, '06-01-2024'), ('105', 'Thomas','David', 'Civil', false, '06-01-2024');

Now, illustrate the data inserted into the ‘student’ table with the following SQL statement’s help.

select * from student;

Example #2

Create a table of name ‘teacher.’


CREATE TABLE teacher ( teacher_id INT NOT NULL PRIMARY KEY, firstname VARCHAR (50) NOT NULL, lastname VARCHAR (50) NOT NULL, branch VARCHAR (50) NOT null, salary numeric ); INSERT INTO teacher (teacher_id, firstname, lastname, branch,salary) values ('1', 'Hugo','Smith', 'Computer',20000), ('2', 'Brayden','Johnson', 'Computer',30000), ('3', 'Ronan','Williams', 'Civil',35000), ('4', 'Antonio','Brown', 'Civil',40000), ('5', 'Marco','Davis', 'Civil',25000);

Now, illustrate the data inserted into the ‘teacher’ table with the following SQL statement’s help.

select * from teacher;

1. PostgreSQL Aliases for column

We specify the alias names to make the column headers more readable in the final result set. Like whenever we use functions like MAX, we can alias the result of the MAX function to make it easier to read.

SELECT firstname, MAX(salary) AS high_salary FROM teacher GROUP BY firstname;

Illustrate the result of the above statement using the following snapshot.

In the above example, we aliased MAX(salary) as ‘high_salary’. Therefore, the column header of the second column will be displayed as ‘high_salary’. In this example, we have not added any space in, so it does not need to add quotes around the given alias_name.

It is acceptable to add quotes around the alias_name in PostgreSQL Alias as follows:

SELECT firstname, MAX(salary) AS "high_salary" FROM teacher GROUP BY firstname;

Illustrate the result of the above statement using the following snapshot.

SELECT firstname, MAX(salary) AS "high salary" FROM teacher GROUP BY firstname;

Illustrate the result of the above statement using the following snapshot.

2. PostgreSQL Aliases for table

We generally use the Alias on the table if we want to abbreviate the name to the table to make the queries more readable and shorter, or in the case of SELF JOIN, where we use the same table multiple times. It is acceptable to define aliases for the tables you want to give a temporary name and not for all tables.

Let’s consider the following example to understand the table alias.

SELECT s.firstname, s.branch, teacher.firstname FROM student s INNER JOIN teacher ON s.branch = teacher.branch ORDER BY s.rollno asc;

Illustrate the result of the above statement using the following snapshot.

In the above statement for the ‘student’ table, we have created Alias s. So in this statement, we can use ‘s’ instead of the student table as it refers to the ‘student’ table.

Now we will add an alias for the ‘teacher’ table as ‘t’; look at the following example.

SELECT s.firstname, s.branch, t.firstname FROM student s INNER JOIN teacher t ON s.branch = t.branch ORDER BY s.rollno asc;

Illustrate the result of the above statement using the following snapshot.

Recommended Articles

We hope that this EDUCBA information on “PostgreSQL Alias” was beneficial to you. You can view EDUCBA’s recommended articles for more information.

How Does Encryption Work?




A B C D E F G H I J K L M N O P Q R S T U V W X Y Z More alphabets and unbreakable encryption

The weaknesses of the Caesar substitution cipher can be slightly alleviated by using more than one shifted alphabet. The example below can be expanded to 26 shifted alphabets of which several are used at once, but not all of them.





Rather than doing a straight substitution this time we use addition, with a twist. Each letter of the alphabet is assigned a number, A is 0, B is 1, C is 2 and so on. I is the 9th letter of the alphabet, which means it has a value of 8. P (the letter below it on our one-time-cipher pad) 15. 8 + 15 = 25 which means X. The second letter of our message is S, which has the value 18. It just so happens that S is also the letter on our one-time pad (which isn’t an issue at all). 18 + 18 = 36. Now here is the twist, there is no 36th letter of the alphabet. So we perform what is called a modulus operation. What that basically means is that we divided the result by 26 (the number of letters in the alphabet) and use the remainder. 36 / 26 = 1 remainder 10. The letter with the value of 10 is K. If you continue doing this the final encrypted message is:

Computers are flexible, unlike mechanical boxes, computers can be programmed to perform lots of different operations on a message and the number and complexity of these operations can be altered relatively quickly.


Computers deal with binary numbers not just letters.

Exclusive OR (XOR) – This is a bit level logical operation that is applied to 2 input bits A and B. The Exclusive OR returns true or false (1 or 0) to the question, “A or B, but not, A and B”. You can think of it as, “one or the other but not both”. So, if A is 1 and B is 0 then that is one or the other, so the result is 1 (true). The same result applies to A is 0 and B is 1. But if A is 0 and B is 0 then the result is 0 (false), as both have the same value. False is also given for A is 1 and B is 1.

But the real magic of XOR is that it is reversible. If A XOR B = C then B XOR C = A, and A XOR C = B. This is very important for encryption as it means that data can be encrypted (where A is the data) using a key (B) to get the encrypted data (C). Later the encrypted data can be decrypted by XOR it with the key again to get the original data. The reason XOR is used in conjunction with complicated round functions and bit shifting operations is because on its own XOR can be broken using frequency analysis (because of the constantly repeating key).

Public key cryptography and wrap-up

Async In Js: How Does It Work

In JavaScript, code execution is single-threaded, which means that only one thing can happen at a time. The JavaScript engine executes code in a sequential and synchronized manner, one line at a time.

This means that if there is a time-consuming task in the code, such as an API call or a long loop, the execution of the entire code will be blocked until the task is completed. During this time, the application will not be able to respond to any user input or execute any other code.

The problem with synchronous code

Synchronous code is easy to understand and follow, as it executes exactly as written, one line after the other.

console.log('one') console.log('two') console.log('three')

We can further illustrate this using the delay() function:

function printDelay() { console.log('Phew!') } delay(5000) printDelay()

In the example above, the code requires a delay of 5 seconds before printing a greeting message to the console. However, the delay function is synchronous, which means that the execution of the entire code is blocked for 5 seconds until the delay function is completed. During this time, the JavaScript engine cannot execute any other code, making the application unresponsive.

Therefore, to avoid blocking the execution of code, developers use asynchronous programming techniques such as callbacks, promises, and async/await to handle long-running tasks in a non-blocking manner.


Asynchronous code, on the other hand, is executed in a non-sequential manner, meaning that the execution of the next line does not wait for the completion of the previous line. Instead, it continues to execute the remaining code while performing other tasks in the background.

Here’s an example of asynchronous code that uses the same setTimeout function to delay the execution of the code for two seconds:

console.log('Start'); console.log('End');

In this example, the setTimeout function is called with a delay of two seconds, but the execution of the code continues without waiting for it.

Therefore, the ‘End’ message is printed immediately after the ‘Start’ message, and the ‘Inside Timeout’ message is printed after two seconds.

Asynchronous code is useful when dealing with time-consuming tasks, such as network requests, file operations, or user interactions. By executing these tasks asynchronously, the rest of the code can continue to execute without being blocked, improving the overall performance and responsiveness of the application.

The call stack

In JavaScript, the call stack is a mechanism used by the interpreter to keep track of the current execution context during code execution. It is essentially a data structure that stores the execution context of a program in a stack-like manner.

Whenever a function is called, the interpreter pushes the function call onto the top of the call stack, along with its associated arguments and variables. The interpreter then executes the function, and when it finishes executing, it pops the function call off the top of the call stack and continues executing the code from where it left off.

This process continues for each function call in the program, with the call stack growing and shrinking as functions are called and returned.

For example, consider the following code:

function foo() { function bar() { console.log('Hello!') } bar() } foo()

In this code, there are three nested functions, foo, bar, and a console.log function that logs the message ‘Hello!’. The foo function calls the bar function, which in turn calls the chúng tôi function.

When the code is executed, the foo function is called first, and the interpreter pushes the foo function call onto the top of the call stack. Within the foo function, the bar function is called, and the interpreter pushes the bar function call onto the top of the call stack, above the foo function call.

When the bar function is called, it executes the chúng tôi function, which logs the message ‘Hello!’ to the console. Once the function is executed, the interpreter pops the function off the top of the call stack and continues executing the bar function.

After the bar function finishes executing, the interpreter pops it off the call stack, and control returns to the foo function, which finishes executing and is then popped off the call stack as well.

Therefore, the call stack would look something like this during execution:

| console.log() | | bar() | | foo() |

After executing the entire block, the stack will become empty.

The entire chain of function calls is stored on the call stack in synchronous code. When a function calls itself repeatedly without any condition to stop, it is called recursion without a base case. This can cause a stack overflow, as each recursive call adds a new function call to the top of the stack, and if the recursion continues indefinitely, the stack will eventually run out of memory, and the program will crash.

Let’s see how the call stack works with asynchronous code:

function main() { setTimeout(function welcome() { console.log('Welcome!') }, 3000) console.log('Goodbye!') } main()

Calling the main() function, the call stack is:


Calling the setTimeout() function, the call stack is:

setTimeout(); main();

setTimeout has finished, it exits the stack:


Calling console.log('Goodbye!'):

console.log('Goodbye!'); main();

The task is complete, exiting the stack:


The main() call is also finished, and the stack becomes empty.

After 3 seconds, the welcome() function is called, and it goes on the stack:


This will call console.log('Welcome!'):

console.log('Welcome!'); welcome();

After it is done, it leaves the stack.


After executing the entire block, the stack becomes empty again.

One thing you might not have noticed right away is that setTimeout() was terminated immediately, even though the callback function wasn’t yet executed, it wasn’t even called!

This has to do with a mechanism known as the event loop, so let’s move on to that!

Event Loop

In JavaScript, setTimeout() is a function that allows developers to schedule a callback function to be executed after a specified delay. However, setTimeout() is not actually part of the JavaScript language itself.

It is a Web API, which means it is a functionality provided by the browser environment rather than the JavaScript engine.

Web APIs are additional features provided by the browser environment that allow JavaScript to interact with the browser and its features, such as timers, intervals, and event handlers. When we use setTimeout(), it interacts with the Web API to schedule the callback function to be executed after the specified delay.

The event loop is a mechanism that is responsible for executing code, collecting and handling events, and executing subtasks from the queue. When an asynchronous event is triggered, such as a setTimeout() callback, it is added to the event queue. The event loop continuously checks the event queue for new events, and when it finds an event, it dequeues it and executes the associated callback function.

Therefore, when we use setTimeout(), it is not executed immediately, but instead scheduled to be executed in the future by the Web API. The event loop is responsible for managing the execution of the callback function by dequeuing it from the event queue and executing it when it is the appropriate time.

It is the event loop that is responsible for setTimeout() missing from the stack in the last example. Now, let’s take the previous example we examined and draw a clear picture of what is happening:

function main() { setTimeout(function welcome() { console.log('Welcome!') }, 3000) console.log('Goodbye!') } main()

To best illustrate how this works, let’s include not only the stack but also the Web API and the task queue that the Web API uses to store what needs to be executed.

Calling: main()

StackWeb APITask Queuemain()

Web API and task queue are empty for now.

Calling: setTimeout()

When setTimeout() disappears from the stack, it enters Web API visibility, where the interpreter understands that there is a welcome() function inside it to be executed after 3 seconds:

StackWeb APITask Queuemain()setTimeout(welcome)

This is followed by a console.log('Goodbye!') call. The setTimeout(welcome) function remains in the Web API. It will be there until 3 seconds have elapsed:

The console.log() has been processed, the main() call ends:

StackWeb APITask Queuemain()setTimeout(welcome)

The main() call has ended, so the stack is emptied, but because 3 seconds haven’t passed yet, the setTimeout(welcome) function is still inside the Web API:

StackWeb APITask QueuesetTimeout(welcome)

Finally, 3 seconds have passed – the welcome() function moves to the task queue:

StackWeb APITask Queuewelcome()

The event loop now moves the welcome() function from the task list to the call stack:

StackWeb APITask Queuewelcome()

This then calls console.log('Welcome!'):

The stack is now empty.

In JavaScript, the call stack and the task queue are two key components of the event loop. The call stack is a last in, first out (LIFO) data structure that tracks the current position of code execution. The task queue, also known as the event queue, is a first in, first out (FIFO) data structure that holds callback functions waiting to be executed.

The call stack and the task queue are named stack and queue because they work on LIFO and FIFO principles, respectively. This means that the most recently added function to the stack is the first to be executed (LIFO), while the first added function to the queue is the first to be executed (FIFO).

This is Loupe, a tool built by Philip Roberts. It is designed to help developers understand how JavaScript’s call stack/event loop/callback queue interact with each other.


In the context of the setTimeout() example, the callback function is the welcome() function, which is executed after a 3-second delay. When the timer expires, the setTimeout() function invokes the welcome() function as a callback.

Initially, callbacks were the only way to handle asynchronous code in JavaScript, and many chúng tôi APIs were designed specifically to work with callbacks. The mental model of callbacks is simple: “execute this function when this event happens.”

However, callbacks can lead to a phenomenon called “callback hell,” which occurs when multiple nested callbacks are used, making the code difficult to read and maintain. This can result in code that is hard to debug and prone to errors.

⇢ Callback Hell

Suppose we have a number of asynchronous tasks that depend on each other: that is, the first task starts a second task when completed, the second task starts a third, etc.

function task1(callback) { /* .. */ callback(); }, 1000); } function task2(callback) { /* .. */ callback(); }, 1000); } function task3(callback) { /* .. */ callback(); }, 1000); } console.log("All tasks completed"); }); }); });

In this example, we have three asynchronous tasks (task1, task2, and task3) that depend on each other. Each task takes a callback function as an argument, which is executed when the task is completed. As you can see, the code becomes deeply nested and harder to read as we chain these tasks together.

The answer to this problem? Promises.


A Promise is a wrapper object for asynchronous code that represents the eventual completion or failure of a single asynchronous operation.

A Promise has three states:

pending – The initial state, neither fulfilled nor rejected.

fulfilled – The operation completed successfully, resulting in a value.

rejected – The operation failed, resulting in an error.

In terms of the event loop, a Promise is similar to a callback. The function to be executed (either resolve or reject) is in the Web API environment, and when the event occurs, it goes to the task queue, from where it goes to the call stack for execution.

Promises introduce a division between macro-tasks and micro-tasks.

A Promise’s then method is a micro-task, which means it is executed before any macro-tasks such as setTimeout. Micro-tasks are added to the micro-task queue, while macro-tasks are added to the macro-task queue.

Here’s an example that demonstrates the use of both resolve and reject in Promises. In this example, we’ll simulate the process of fetching user data based on a user ID. If the user is found, we’ll resolve the Promise, otherwise, we’ll reject it.

function getUserData(userId) { const users = { 1: { id: 1, name: "Alice" }, 2: { id: 2, name: "Bob" }, }; const user = users[userId]; if (user) { resolve(user); } else { reject(new Error("User not found")); } }, 1000); }); } getUserData(1) console.log("User data:", userData); }) console.error("Error:", error); }); getUserData(3) console.log("User data:", userData); }) console.error("Error:", error); });

In this example, getUserData returns a Promise that simulates an asynchronous operation to fetch user data. After a 1-second delay, the Promise is either fulfilled with the user data (using resolve()) or rejected with an error message (using reject()).

We call getUserData() with two different user IDs: 1 and 3. For user ID 1, the Promise is resolved and the user data is logged. For user ID 3, the Promise is rejected and the error message is logged. We use the then() method to handle the fulfilled Promise and the catch() method to handle the rejected Promise.

Here’s a comparison between the callback-based code and the Promise-based code:

Callback-based code:

console.log(“All tasks completed”); }); }); });

Promise-based code:

task1() return task2(); }) return task3(); }) console.log("All tasks completed"); });

Code conciseness – Although Promises improve code readability compared to callbacks, they can still result in more verbose code than desired, especially when dealing with multiple chained operations.

Debugging limitations – When using arrow functions and chaining Promises, you might face difficulties in setting breakpoints for debugging since there is no function body. To overcome this limitation, you would need to expose the function, which makes the code less concise.

Error stack – When an error occurs within a Promise chain, the error stack may contain several then() calls, making it harder to pinpoint the exact location of the error.

Nested conditions – Handling complex conditional logic within Promises can lead to nested structures, increasing the amount of code and reducing readability.

To address these issues, JavaScript introduced async/await, a more concise and readable syntax for working with Promises. With async/await, you can write asynchronous code that looks and behaves like synchronous code, making it easier to read, debug, and maintain.

Asynchronous functions

In short, asynchronous functions are functions that return promises.

An asynchronous function is marked with a special keyword async:

async function request() {} class MainClass { async request() {} }

They always return a Promise. Even if we didn’t explicitly specify it, as in the examples above, they would still return a Promise when called.

async function request() {}

However, asynchronous functions can be handled without then().

Bundling async/await

Within asynchronous functions, you can call other asynchronous functions without using then() or callbacks, but with the help of the await keyword.

async function loadUsers() { const response = await fetch('/api/users/') const data = await response.json() return data }

In the example above, we use the fetch() method inside the loadUsers() function.

We call all asynchronous functions inside with await - that way, the Promise function returns are automatically expanded, and we get the value that was inside the Promise.

The pros of async/await

Async/await provides several benefits over using Promises with then() chains when working with asynchronous code:

Cleaner and shorter code - Async/await helps you write cleaner and shorter code by eliminating the need for chaining then() methods. It flattens the structure, making it more readable and resembling synchronous code.

Improved handling of conditions and nested constructs - With async/await, it becomes easier to work with conditional statements and nested constructs, as you can use familiar constructs like if, else, and loops in conjunction with await. This improves the readability of the code and simplifies its structure.

Familiar error handling with try-catch - Async/await allows you to handle errors using try-catch blocks, similar to how you would handle errors in synchronous code. This brings consistency in error handling and makes it easier to reason about the flow of the code when an error occurs.

Here's an example demonstrating the benefits of async/await:

async function fetchData(id) { const data = { 1: "Success", 2: "Error", }; if (data[id] === "Success") { resolve(data[id]); } else { reject(new Error("Fetch error")); } }, 1000); }); } async function main() { try { const id = 1; const result = await fetchData(id); if (result === "Success") { console.log("Data fetched successfully"); } else { console.log("Data fetch failed"); } } catch (error) { console.error("Error:", error); } } main();

In this example, we have an async function fetchData() that simulates an asynchronous operation. The main() function uses async/await to call fetchData(). We use a try-catch block for error handling and a simple if statement to check the result of the data fetch.

The code structure is clear, flat, and easy to read, showcasing the benefits of async/await over Promise chaining.


In conclusion, asynchronous programming is an essential aspect of JavaScript, as it enables handling tasks such as network requests, file I/O, and timers without blocking the main thread. Throughout the years, various techniques have been introduced to manage asynchronous code in JavaScript, including callbacks, Promises, and async/await.

Callbacks were the initial approach, but they led to issues like callback hell, where deeply nested and hard-to-read code structures emerged. To address these problems, Promises were introduced, providing a more manageable way to work with asynchronous operations. Promises led to cleaner, more readable code and improved error handling. However, they still had some drawbacks, such as verbosity and difficulty in handling complex nested conditions.

To further simplify asynchronous code, JavaScript introduced async/await, a syntax that allows writing asynchronous code resembling synchronous code. This approach offers several benefits, including cleaner and shorter code, better handling of conditions and nested constructs, and familiar error handling with try-catch blocks.

By understanding the evolution of asynchronous programming in JavaScript and how each technique works, you can learn to write more efficient, maintainable, and readable code. Embracing async/await can significantly improve the overall experience of working with asynchronous operations in JavaScript applications.

How Does Prolog Programming Work?

Introduction to Prolog Programming

Web development, programming languages, Software testing & others

Why we need Prolog programming?

It is a first-order logic programming language.

The programmer does not require complex algorithms and coding.

this programming is easier for Pattern matching and searching data than in other programming languages.

It works with abstract models and deals with objects.

This programming is the prolog is an easy, simple, and formula-based programming language.

This programming separates logics and controls of the data.

The prologs are based on the symbolic and computation for manipulation.

It is based on numerical data for pattern matching.

This programming language is a relational database for maintaining a memory management system.

It provides small syntaxes and small coding patterns.

It is based on the searching tree of determination and possibility.

The prolog supports a lot of the extensions like socket interface, global variables, and operating system interfaces.

It supports the native code compiler and becomes a stand-alone programming language.

The prolog supports constraints like arithmetic, Boolean, symbolic, etc.

This programming provides an efficient constraint solver and makes compatibility with integrals and variables.

The prolog supports compiled predicates and consulted predicates in the compiler.

This programming supports powerful interfaces between the prolog and C programming language.

How does Prolog programming works?

The following shows the prologue programming’s working environment.

Download or save the software in the required path.

The downloaded software saves in the exe file format to install the software.

The working procedure of the prolog programming shows below.

The warren abstract machine (WAM) is working as a compiler for prolog language.

The compiler file is converted into independent low-level machine language.

The resulting file again translates into the target machine.

This working procedure makes a stand-alone programming language.

Advanced prolog programming requires constraints for problem-solving.

You choose arithmetic constraints, Boolean constraints, and symbolic constraints, and so on.

The basic procedure of this programming is shown below.

The prolog software creates a file with the chúng tôi format. The “*.pl” is an extension of the prolog file.

Write the syntax of the prolog programming language. You can use many entities in single relation.

It shows the basic syntax below.

Format –

relation (objects or data). relation (data1, data2, data).

The prolog programming example shows the basic output.




The Prolog programming software is easy to build and install the software.

This programming language does not need lengthy syntax and coding.

This compiler is working fast and hustle-free.

It has powerful interfaces between the prolog and C programming language.

The user of prolog programmer creates their constraints with customization.

The prolog programmer provides several extensions.

This programming language provides high-level and categorized constraints.

The prolog supports easier pattern matching and searching data features.

The prolog language does not use complex algorithms and coding.

This language maintains and operates data lists with easy coding.

The several algorithm and code for input and output procedures are not easy.

The LISP language dominates and overcomes input and output features.

It does not support graphics features. If you need graphics, then you must use turbo prolog.

The prolog order affects the efficiency of the programming language.

This language does not support the “OR” logical conditions. This language does not support multiple true statements.

This language does not support the “NOT” logical condition. This language does not support negative statements.

The prolog is first-order logic language. This language does not allow second-order logic directly.


It is mostly usable in artificial intelligence technology.

This programming language uses for pattern matching using the parse tree feature.

The prolog is used in the computation schematics.

This programming uses in the problem-solving and understanding of natural language.

This language uses planning and designing of the robot.

The prolog is used in the automation system and theorem proving with a variety of constraints.

The prolog is a useful language in machine learning and the graphical user interface.

This is used for expert system and term rewriting using arithmetic and symbolic constraints.


It is simple planning and problem-solving programming language.

This language is used for AI, machine learning, and computational technology.

The prolog works in large-size applications in small and easy coding.

Recommended Articles

We hope that this EDUCBA information on “Prolog Programming” was beneficial to you. You can view EDUCBA’s recommended articles for more information.

Update the detailed information about How Does Generic Class Work In Scala? on the website. We hope the article's content will meet your needs, and we will regularly update the information to provide you with the fastest and most accurate information. Have a great day!