惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Spread Privacy
Spread Privacy
K
Kaspersky official blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Forbes - Security
Forbes - Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
The Last Watchdog
The Last Watchdog
SecWiki News
SecWiki News
Attack and Defense Labs
Attack and Defense Labs
Google DeepMind News
Google DeepMind News
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Secure Thoughts
WordPress大学
WordPress大学
Microsoft Security Blog
Microsoft Security Blog
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
V
Visual Studio Blog
Security Latest
Security Latest
TaoSecurity Blog
TaoSecurity Blog
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
Cloudbric
Cloudbric
aimingoo的专栏
aimingoo的专栏
S
Schneier on Security
N
Netflix TechBlog - Medium
MyScale Blog
MyScale Blog
T
The Blog of Author Tim Ferriss
H
Hacker News: Front Page
C
Cybersecurity and Infrastructure Security Agency CISA
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
AWS News Blog
AWS News Blog
AI
AI
G
GRAHAM CLULEY
IT之家
IT之家
P
Privacy & Cybersecurity Law Blog
L
Lohrmann on Cybersecurity
Last Week in AI
Last Week in AI
D
Docker
Recent Announcements
Recent Announcements
O
OpenAI News
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
S
Security @ Cisco Blogs
T
Troy Hunt's Blog
C
Check Point Blog
博客园 - 三生石上(FineUI控件)
A
About on SuperTechFans
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
N
News and Events Feed by Topic

Dmitri Pavlutin Blog

Pure Functions in JavaScript: A Beginner's Guide Record Type in TypeScript: A Quick Intro How to Write Comments in React: The Good, the Bad and the Ugly 4 Ways to Create an Enum in JavaScript React forwardRef(): How to Pass Refs to Child Components TypeScript Function Types: A Beginner's Guide How to Use v-model to Access Input Values in Vue Mastering Vue refs: From Zero to Hero Environment Variables in JavaScript: process.env 5 Must-Know Differences Between ref() and reactive() in Vue How to Destructure Props in Vue (Composition API) Triangulation in Test-Driven Development How to Use nextTick() in Vue Programming to Interface Vs to Implementation A Smarter JavaScript Mapper: array.flatMap() Array Grouping in JavaScript: Object.groupBy() How to Access ES Module Metadata using import.meta JSON Modules in JavaScript How to Trim Strings in JavaScript TypeScript Function Overloading How to Debounce and Throttle Callbacks in Vue How to Show/Hide Elements in Vue Sparse vs Dense Arrays in JavaScript How to Fill an Array with Initial Values in JavaScript Covariance and Contravariance in TypeScript What are Higher-Order Functions in JavaScript? How to Use TypeScript with React Components Index Signatures in TypeScript How to Use React useReducer() Hook unknown vs any in TypeScript A Guide to React Context and useContext() Hook How to Use Promise.any() 2 Ways to Remove a Property from an Object in JavaScript 'return await promise' vs 'return promise' in JavaScript How to Use Promise.allSettled() How to Use fetch() with JSON JavaScript Promises: then(f,f) vs then(f).catch(f) How to Use Promise.all() A Simple Guide to Component Props in React Don't Stop Me Now: How to Use React useTransition() hook A Simple Explanation of JavaScript Variables: const, let, var ES Modules Dynamic Import How to Memoize with React.useMemo() How to Cleanup Async Effects in React Why Math.max() Without Arguments Returns -Infinity How to Debounce and Throttle Callbacks in React Don't Confuse Function Expressions and Function Declarations in JavaScript How to Use ES Modules in Node.js Solving a Mystery Behavior of parseInt() in JavaScript How to Use Array Reduce Method in JavaScript 3 Ways to Merge Arrays in JavaScript A Guide to Jotai: the Minimalist React State Management Library The Difference Between Values and References in JavaScript How to Implement a Queue in JavaScript A Helpful Algorithm to Determine "this" value in JavaScript React useRef() Hook Explained in 3 Steps 7 Interview Questions on "this" keyword in JavaScript. Can You Answer Them? How to Greatly Enhance fetch() with the Decorator Pattern 7 Interview Questions on JavaScript Closures. Can You Answer Them? What's a Method in JavaScript? array.sort() Does Not Simply Sort Numbers in JavaScript How to Solve the Infinite Loop of React.useEffect() The New Array Method You'll Enjoy: array.at(index) What's the Difference between DOM Node and Element? Why Promises Are Faster Than setTimeout()? Everything About Callback Functions in JavaScript How React Updates State 5 Mistakes to Avoid When Using React Hooks 5 Best Practices to Write Quality JavaScript Variables Type checking in JavaScript: typeof and instanceof operators 3 Ways to Check if a Variable is Defined in JavaScript React Forms Tutorial: Access Input Values, Validate, Submit Forms Prototypal Inheritance in JavaScript How to Timeout a fetch() Request How to Learn JavaScript If You're a Beginner A Simple Explanation of React.useEffect() A Simple Explanation of JavaScript Iterators How to Use React Controlled Inputs Everything about null in JavaScript How to Use Fetch with async/await Getting Started with Arrow Functions in JavaScript An Interesting Explanation of async/await in JavaScript Front-end Architecture: Stable and Volatile Dependencies Is it Safe to Compare JavaScript Strings? How to Access Object's Keys, Values, and Entries in JavaScript What Actually is a String in JavaScript? 3 Ways to Shallow Clone Objects in JavaScript (w/ bonuses) Checking if an Array Contains a Value in JavaScript JavaScript Event Delegation: A Beginner's Guide How to Parse URL in JavaScript: hostname, pathname, query, hash 3 Ways to Detect an Array in JavaScript How to Get the Screen, Window, and Web Page Sizes in JavaScript 3 Ways to Check If an Object Has a Property/Key in JavaScript How to Compare Objects in JavaScript Object.is() vs Strict Equality Operator in JavaScript Own and Inherited Properties in JavaScript 5 Differences Between Arrow and Regular Functions How to Use Object Destructuring in JavaScript Your Guide to React.useCallback() 5 JavaScript Scope Gotchas
What is a Promise in JavaScript?
Dmitri Pavlutin · 2021-07-15 · via Dmitri Pavlutin Blog

I had had difficulties in understanding promises when I had been learning them back in a few years.

The problem was that most of the tutorials were solely describing the promise object, its methods, etc. But I don't care much about promises, I care about them as long as they make coding easier!

What follows is the post that I had wanted to read to understand promises myself. The post describes why promises make coding asynchronous logic easier, then explains how to use promises properly, including with the async/await syntax.

Table of Contents

  • 1. Why promises
    • 1.1 The callbacks approach
    • 1.2 Encapsulating asynchronicity
  • 2. What is a promise
    • 2.1 Extracting the promise fulfill value
    • 2.2 Extracting the promise rejection error
    • 2.3 Extracting value and error
  • 3. Chain of promises
  • 4. async/await
    • 4.1 await-ing promise value
    • 4.2 catch-ing promise error
    • 4.3 await-ing chain
  • 5. Conclusion

1. Why promises

JavaScript works well with imperative and synchronous code.

Let's consider a function findPerson(who) that determines whether a person name is contained in a list of persons:


function getList() {

return ['Joker', 'Batman'];

}

function findPerson(who) {

const list = getList();

const found = list.some(person => person === who);

console.log(found); // logs true

}

findPerson('Joker');


Open the demo.

The snippet above is synchronous and blocking. When JavaScript enters into findPerson() function, it doesn't get out of there until the function is executed.

Getting the list of persons const list = getList() is a synchronous operation too.

Synchronous code is straightforward. But you don't always have the luck to access data instantly: some data, like fetching data over the network, could take a while to be available.

For example, let's say that accessing the list of persons from getList() is an operation that requires, for example, 1 second.


function getList() {

setTimeout(() => {

['Joker', 'Batman'] // How to return the list?

}, 1000);

}

function findPerson(who) {

const list = /* How to access the list? */;

const found = list.some(person => person === who);

console.log(found);

}

findPerson('Joker'); // logs true


How to return the list of persons from getList() with a delay of 1 second? Same way, how would findPerson(who) access the delayed list?

Unfortunately, now things have become more complicated. Let's see a few solutions how to code the delayed list.

1.1 The callbacks approach

One classic approach would be to introduce callbacks:


function getList(callback) {

setTimeout(() => callback(['Joker', 'Batman']), 1000);

}

function findPerson(who) {

getList(list => {

const found = list.some(person => person === who);

console.log(found); // logs true

});

}

findPerson('Joker');


Open the demo.

getList(callback) becomes more complex because it needs one more argument: the callback function.

Also, inside findPerson(who) you have to supply a callback getList(list => { ... }) to access the list properly.

The code using callbacks is more difficult to follow because the flow of the computation is hidden in between callbacks. If you'd need to manage many asynchronous operations using callbacks, you could quickly end with the callback hell problem.

While callbacks have their good place in JavaScript, still, let's find a better solution.

1.2 Encapsulating asynchronicity

Synchronous code is easy to understand. You see line by line how the code is executed.

How to code asynchronous operations, while still preserving the readability of synchronous code?

What about returning from getList() a kind-of list of persons?

This kind-of list of persons is then kind-of checked if contains who, and then the boolean value is logged to console. What's great is that these kind-of results can be returned, assigned to variables just like regular objects.

This kind-of result object that encapsulates (aka holds, manages, contains) the result of an asynchronous operation is a promise object.

Promises, wrapping the asynchronous operation result, can be returned synchronously from a function, assigned to variables, or used as arguments. That's the idea of promises: encapsulate the asynchronicity and allow function handling asynchronous operations to still look synchronous.

2. What is a promise

A promise is an object that encapsulates the result of an asynchronous operation.

Each promise has state, which can have one of the following values:

  • Pending
  • Fullfilled with a value
  • Rejected for a reason

The just created promise is in a pending state. The promise maintains the pending state as long as the asynchronous operation behind is in progress.

Then, depending on how the asynchronous operation completes, the promise state changes to either:

A) fulfilled (when the async operation completed successfully)

Promise fulfilled state

B) or rejected (when then async operation failed).

Promise rejected state

In JavaScript, you can create a promise object using a special constructor Promise:


const promise = new Promise((resolve, reject) => {

// Async operation logic here....

if (asyncOperationSuccess) {

resolve(value); // async operation successful

} else {

reject(error); // async operation error

}

});


Promise constructor accepts a special function that should contain the logic of the asynchronous operation.

In the special function, after the completion of the operation:

  1. If the async operation completes successfully, call resolve(value) — which changes the state of the promise to fulfilled with value
  2. Otherwise, in case of an error, call reject(error) — changing the state of the promise to rejected with the error reason.

Let's make a pause from dry theory and get back to the persons' example.

Like I mentioned before, I want the function getList() to return a kind-of persons — a promise of list of persons:


function getList() {

return new Promise(resolve => {

setTimeout(() => resolve(['Joker', 'Batman']), 1000);

});

}


getList() creates and returns a promise. Inside of the promise, after passing 1 second, calling resolve(['Joker', 'Batman']) effectively makes the promise fulfill with the list of persons.

While in examples that follow I'm creating promises by hand, usually you won't do this in production. Most of the asynchronous functions of popular libraries (like axios) or web APIs (like fetch()) return already constructed promises.

You can access the fulfill value of a promise (in simple words, the result of a successfully completed async operation) using a special method:


promise

.then(value => {

// use value...

});


Promise then()

Here's how to access the value of the promise returned by getList():


function getList() {

return new Promise(resolve => {

setTimeout(() => resolve(['Joker', 'Batman']), 1000);

});

}

const promise = getList();

promise

.then(value => {

console.log(value); // logs ['Joker', 'Batman']

});


Open the demo.

Having the knowledge of how to extract a fulfilled value from a promise, let's transform findPerson(who) to extract the list from the promise returned by getList():


function getList() {

return new Promise(resolve => {

setTimeout(() => resolve(['Joker', 'Batman']), 1000);

});

}

function findPerson(who) {

const listPromise = getList();

listPromise

.then(list => {

const found = list.some(person => person === who);

console.log(found); // logs true

});

}

findPerson('Joker');


Open the demo.

Look closer at const listPromise = getList() statement: you get the promise using a synchronous statement, even if behind it runs an asynchronous operation.

That's the first big benefit of promises (the second big benefit is chaining): you can manipulate encapsulated async operation results in a sync way, without over-complicating the functions like the callbacks approach would do.

The promise rejects with an error if the operation completes unsuccessfully. You can access the rejection error using a special method:


promise

.catch(error => {

// check error...

})


Promise catch()

For example, let's imagine that accessing the list of persons ends in an error (note the use of reject(error) function):


function getList() {

return new Promise((resolve, reject) => {

setTimeout(() => reject(new Error('Nobody here!')), 1000);

});

}

const promise = getList();

promise

.catch(error => {

console.log(error); // logs Error('Nobody here!')

});


Open the demo.

This time promise is rejected with new Error('Nobody here!'). You can access that error in the callback supplied to promise.catch(errorCallback).

2.3 Extracting value and error

You can also extract the fulfill value and the reject reason at once. You can do so in 2 ways:

A) Supply 2 callbacks to promise.then(successCallback, errorCallback) method. The first callback successCallback is called when the promise is fulfilled, while the second errorCallback when rejected:


promise

.then(value => {

// use value...

}, error => {

// check error...

});


B) or you can use what is called chain of promises (described below) and chain promise.then(successCallback).catch(errorCallback):


promise

.then(value => {

// use value...

})

.catch(error => {

// check error...

});


Let's look closer at approach B) since it's used more often.

When using promise.then(successCallback).catch(errorCallback) chain, if promise resolves successfully then only successCallback is called:


function getList() {

return new Promise(resolve => {

setTimeout(() => resolve(['Joker', 'Batman']), 1000);

});

}

const promise = getList();

promise

.then(value => {

console.log(value); // logs ['Joker', 'Batman']

})

.catch(error) => {

console.log(error); // Skipped...

};


Open the demo.

However, in case if promise rejects, then only errorCallback is called:


function getList() {

return new Promise((resolve, reject) => {

setTimeout(() => reject(new Error('Nobody here!')), 1000);

});

}

const promise = getList();

promise

.then(value => {

console.log(value); // Skipped...

})

.catch(error => {

console.log(error); // logs Error('Nobody here!')

});


Open the demo.

3. Chain of promises

As seen above, a promise encapsulates the result of an asynchronous operation. You can use anyhow you want a promise: return from a function, use as an argument, assign to variables. That's the first benefit.

The second big benefit is that promises can create chains to handle multiple dependent asynchronous operations.

The technical side of chaining consists of the fact that promise.then(successCallback), and even promise.catch(errorCallback) methods by themselves return a promise, to which you can attach .then() or .catch() methods, and so on.

For example, let's create an async function that doubles a number with a delay of 1 second:


function delayDouble(number) {

return new Promise((resolve, reject) => {

setTimeout(() => resolve(2 * number), 1000);

});

}


Then, let's double 3 times the number 5:


delayDouble(5)

.then(value1 => {

console.log(value1); // logs 10

return delayDouble(value1);

})

.then(value2 => {

console.log(value2); // logs 20

return delayDouble(value2);

})

.then(value3 => {

console.log(value3); // logs 40

});


Open the demo.

Each double operation requires 1 second. The chain performs 3 double operations, and the result of each operation is used by the next operation.

Chain of promises

In a chain of promises, if any promise in the chain rejects, then the resolving flow jumps to the first .catch(), bypassing all .then() in between:


delayDouble(5)

.then(value1 => {

console.log(value1); // logs 10

return new Promise((_, reject) => reject(new Error('Oops!')));

})

.then(value2 => {

console.log(value2); // Skipped...

return delayDouble(value2);

})

.then(value3 => {

console.log(value3); // Skipped...

})

.catch(error => {

console.log(error); // logs Error('Oops!')

});


Open the demo.

4. async/await

While looking at the previous code samples that use promises, you might wonder:

Using promises still requires callbacks and relatively lots of boilerplate code like .then(), .catch().

Your observation would be reasonable.

Fortunately, JavaScript has made a step forward in improving, even more, the asynchronous code by providing the async/await syntax — a really useful syntactic sugar on top of promises.

When possible, I highly recommend working with async/await syntax rather than dealing with raw promises.

Applying the async/await syntax on top of promises is relatively easy:

  • Mark the functions that use promises with the async keyword
  • Inside of the async function body, whenever you want to wait for a promise to resolve, use await promiseExpression syntax
  • An async function always returns a promise, which enables calling async functions inside async functions.

4.1 await-ing promise value

If the promise is fulfilled await promise statement evaluates to the fulfill value:


async function myFunction() {

// ...

const value = await promise;

}


JavaScript async await of promise

When JavaScript encounters await promise, where promise is pending, it's going to pause the function execution until promise gets either fulfilled or rejected.

Now let's use async/await syntax to access the delayed list:


function getList() {

return new Promise(resolve => {

setTimeout(() => resolve(['Joker', 'Batman']), 1000);

});

}

async function findPerson(who) {

const list = await getList();

const found = list.some(person => person === who);

console.log(found); // logs true

}

findPerson('Joker');


Open the demo.

async findPerson(who) pauses its execution for 1 second at the await getList() statement, until the promise getList() is fulfilled.

After the promise being fulfilled, the expression async findPerson(who) evaluates to the actual list of persons.

Looking at the async findPerson(who) function you would notice how similar it is to the synchronous version of that function from the beginning of the post! That's the goal of promises and async/await syntax.

4.2 catch-ing promise error

If the promise rejects while being awaited, you can easily catch the error by wrapping await promise into a try/catch clause:


async function myFunction() {

// ...

try {

const value = await promise;

} catch (error) {

// check error

error;

}

}


JavaScript async catch

For example, let's reject the promise that should return the list of persons:


function getList() {

return new Promise((resolve, reject) => {

setTimeout(() => reject(new Error('Nobody here!')), 1000);

});

}

async function findPerson(who) {

try {

const list = await getList();

const found = list.some(person => person === who);

console.log(found);

} catch (error) {

console.log(error); // logs Error('Nobody here!')

}

}

findPerson('Joker');


Open the demo.

This time the promise await getList() rejects. Right away the execution jumps into catch(error): where error indicates the rejection reason — new Error('Nobody here!').

4.3 await-ing chain

You can use as many await statements as you need inside of an async function. For example, let's transform the example from chain of promises section to async/await syntax:


function delayDouble(number) {

return new Promise((resolve, reject) => {

setTimeout(() => resolve(2 * number), 1000);

});

}

async function run() {

const value1 = await delayDouble(5);

console.log(value1); // logs 10

const value2 = await delayDouble(value1);

console.log(value2); // logs 20

const value3 = await delayDouble(value2);

console.log(value3); // logs 40

}

run();


Open the demo.

Clearly, async/await greatly simplifies handling multiple dependent async operations.

5. Conclusion

The promise is a placeholder holding the result of an asynchronous operation. If the operation completes successfully, then the promise fulfills with the operation value, but if the operation fails: the promise rejects with the reason of the failure.

Promises can also create chains, which are useful in handling multiple dependent async operations.

If you'd like to read more about promises and async/await from a practical side, I recommend checking How to Use Fetch with async/await.

Challenge: do you know the one important difference between promise.then(fn1, fn2) and promise.then(fn1).catch(fn2)? Please share your opinion in a comment below!