Key points of JavaScript asynchronous programming
This article will discuss JavaScript asynchronous programming in an easy-to-understand manner, covering three main methods: callback function, Promise and async/await. We will help you master the core concepts of JavaScript asynchronous programming through sample code, key points summary and in-depth learning resource links.
Summary of content:
Async features of JavaScript
JavaScript is often referred to as an "asynchronous" language. what does that mean? How does it affect development? What changes have occurred in its methods in recent years?
Consider the following code:
result1 = doSomething1(); result2 = doSomething2(result1);
Most languages process every line of code synchronously. The first line runs and returns the result, and the second line will only run after the first line is completed, regardless of how long it takes for the first line.
Single-threaded processing
JavaScript runs on a single thread. When executed in the browser tab, all other operations stop. This is necessary because changes to the page DOM cannot occur on parallel threads; it will be dangerous if one thread redirects to a different URL while another thread tries to attach child nodes.
Users rarely notice this because the processing proceeds quickly in small blocks. For example, JavaScript detects a button click, runs the calculation, and updates the DOM. Once done, the browser can process the next item in the queue.
(Note: Other languages, such as PHP, also use single threads, but may be managed by multi-threaded servers (such as Apache). Two simultaneous requests for the same PHP page can start two threads and run PHP run quarantine instance. )
Use callback function to implement asynchronous operation
Single threading brings a problem. What happens when JavaScript calls a "slow" process, such as Ajax requests from a browser or database operations on the server? This operation can take seconds or even minutes. The browser will be locked while waiting for a response. On the server, the Node.js application will not be able to process further user requests.
The solution is asynchronous processing. Instead of waiting for completion, it tells a process to call another function when the result is ready. This is called a callback function, which is passed as an argument to any asynchronous function.
Example:
doSomethingAsync(callback1); console.log('finished'); // 当doSomethingAsync完成时调用 function callback1(error) { if (!error) console.log('doSomethingAsync complete'); }
doSomethingAsync function accepts a callback function as an argument (only passing a reference to the function, so the overhead is small). It doesn't matter how long doSomethingAsync takes; we only know that callback1 will be executed at some point in the future. The console will display:
result1 = doSomething1(); result2 = doSomething2(result1);
You can read more about callback functions: Understand JavaScript callback functions
Callback hell
Usually, the callback function is called only by one asynchronous function. Therefore, a concise anonymous inline function can be used:
doSomethingAsync(callback1); console.log('finished'); // 当doSomethingAsync完成时调用 function callback1(error) { if (!error) console.log('doSomethingAsync complete'); }
A series of asynchronous calls can be completed by nesting callback functions. For example:
<code>finished doSomethingAsync complete</code>
Unfortunately, this introduces callback hell - a notorious concept that even has its own webpage! The code is hard to read and gets worse when adding error handling logic.
Callback hell is relatively rare in client encoding. If you are making an Ajax call, updating the DOM and waiting for the animation to complete, it may go into two to three layers, but is usually still manageable.
For operating system or server processes, the situation is different. Node.js API calls may receive file uploads, update multiple database tables, write logs, and make further API calls before sending a response.
You can read more about callback hell: Say goodbye to callback hell
Promise
ES2015 (ES6) introduces Promise. The underlying layer still uses callback functions, but Promise provides a clearer syntax to chainedasynchronous commands, making them run in order (more on the next section).
In order to enable Promise-based execution, asynchronous callback-based functions must be changed so that they immediately return a Promise object. The object promises to run one of two functions at some point in the future (passed as a parameter):
In the following example, the database API provides a connect method that accepts callback functions. The external asyncDBconnect function returns a new promise immediately and runs resolve or reject after a connection is established or failed:
doSomethingAsync(error => { if (!error) console.log('doSomethingAsync complete'); });
Node.js 8.0 provides a util.promisify()
utility for converting callback-based functions into Promise-based alternatives. There are two conditions:
Example:
async1((err, res) => { if (!err) async2(res, (err, res) => { if (!err) async3(res, (err, res) => { console.log('async1, async2, async3 complete.'); }); }); });
Async chain call
Anything that returns a Promise can initiate a series of asynchronous function calls defined in the .then()
method. Each function receives the result from the previous resolve:
const db = require('database'); // 连接到数据库 function asyncDBconnect(param) { return new Promise((resolve, reject) => { db.connect(param, (err, connection) => { if (err) reject(err); else resolve(connection); }); }); }
sync function can also be executed in the .then()
block. The return value is passed to the next .then()
(if any).
.catch()
method defines a function that is called when any previous reject is triggered. At this time, no .then()
methods will be run again. You can use multiple .catch()
methods throughout the chain to catch different errors.
ES2018 introduces a .finally()
method that runs any final logic regardless of the result—for example, cleaning up, closing database connections, and more. It is supported in all modern browsers:
result1 = doSomething1(); result2 = doSomething2(result1);
The future of Promise?
Promise reduces callback hell, but also brings its own problems.
Tutorials often do not mention that the entire Promise chain is asynchronous. Any function that uses a series of promises should return its own promise, or run the callback function in the final , .then()
or .catch()
methods. .finally()
You can read more about Promise:
Overview of JavaScript Promise
async/await
Promise can be daunting, so ES2017 introduces async and await. While it may be just syntactic sugar, it makes Promise easier to use and you can avoid the chain altogether. Consider the following Promise-based example: .then()
doSomethingAsync(callback1); console.log('finished'); // 当doSomethingAsync完成时调用 function callback1(error) { if (!error) console.log('doSomethingAsync complete'); }
<code>finished doSomethingAsync complete</code>
async/await code may not be shorter, but there are many benefits:
Upgrade of Promise
async/await depends on the Promise, which ultimately depends on the callback function. This means you still need to understand how Promise works.In addition, when multiple asynchronous operations are processed, there is no direct equivalent of Promise.all or Promise.race. It's easy to forget Promise.all, which is more efficient than using a series of irrelevant await commands.
Limitations of try/catch
If you omit any try/catch around await that fails, the async function will exit silently. If you have a long list of asynchronous await commands, you may need multiple try/catch blocks.One alternative is a higher-order function, which catches errors, making the try/catch block unnecessary (thanks to @wesbos for suggestions).
However, this option may not be practical if the application must react to some errors in a different way than other errors.
Despite some shortcomings, async/await is an elegant addition to JavaScript.
You can read more about using async/await: JavaScript async/await Getting Started Guide with Examples
JavaScript Asynchronous Programming Journey
In JavaScript, asynchronous programming is an inevitable challenge. Callback functions are essential in most applications, but they are easy to get stuck in deeply nested functions.
Promise abstracts the callback function, but there are many syntax traps. Converting existing functions can be a chore, and the .then() chain still looks messy.
Luckily, async/await brings clarity. The code looks synchronous, but it cannot be processed exclusively by a single thread. It will change the way you write JavaScript and may even make you appreciate Promise – if you haven’t before!
(The same FAQ part as the original FAQs should be added here)
Please note that I have tried my best to rewrite the text as per your requirements and retain the original format and location of the image. Since I don't have the ability to access external links, I can't verify the validity of the image links, nor can I add the links you requested. Please check and add the necessary links yourself.
The above is the detailed content of Flow Control in JavaScript: Callbacks, Promises, async/await. For more information, please follow other related articles on the PHP Chinese website!