Home Web Front-end JS Tutorial Analyzing callbacks and code design patterns in Node.js asynchronous programming_node.js

Analyzing callbacks and code design patterns in Node.js asynchronous programming_node.js

May 16, 2016 pm 03:15 PM
node callback asynchronous

The biggest selling point of NodeJS - event mechanism and asynchronous IO, are not transparent to developers. Developers need to write code asynchronously to take advantage of this selling point, which has been criticized by some NodeJS opponents. But no matter what, asynchronous programming is indeed the biggest feature of NodeJS. Without mastering asynchronous programming, you cannot say that you have truly learned NodeJS. This chapter will introduce various knowledge related to asynchronous programming.

In code, the direct manifestation of asynchronous programming is callbacks. Asynchronous programming relies on callbacks, but it cannot be said that the program becomes asynchronous after using callbacks. We can first look at the following code.

function heavyCompute(n, callback) {
 var count = 0,
  i, j;

 for (i = n; i > 0; --i) {
  for (j = n; j > 0; --j) {
   count += 1;
  }
 }

 callback(count);
}

heavyCompute(10000, function (count) {
 console.log(count);
});

console.log('hello');

Copy after login
100000000
hello
Copy after login

As you can see, the callback function in the above code is still executed before subsequent code. JS itself runs in a single thread, and it is impossible to run other code before a piece of code has finished running, so there is no concept of asynchronous execution.

However, if what a function does is to create another thread or process, do something in parallel with the JS main thread, and notify the JS main thread when the thing is done, then the situation is different. Let's take a look at the following code.

setTimeout(function () {
 console.log('world');
}, 1000);

console.log('hello');

Copy after login
hello
world
Copy after login


This time you can see that the callback function is executed after the subsequent code. As mentioned above, JS itself is single-threaded and cannot be executed asynchronously. Therefore, we can think that what special functions provided by the running environment outside of JS specifications such as setTimeout do is to create a parallel thread and return immediately, allowing the JS master to The process can then execute subsequent code and execute the callback function after receiving notification from the parallel process. In addition to the common ones such as setTimeout and setInterval, such functions also include asynchronous APIs provided by NodeJS such as fs.readFile.

In addition, we still return to the fact that JS runs in a single thread, which determines that JS cannot execute other code, including callback functions, before executing a piece of code. In other words, even if the parallel thread completes its work and notifies the JS main thread to execute the callback function, the callback function will not start execution until the JS main thread is idle. The following is such an example.

function heavyCompute(n) {
 var count = 0,
  i, j;

 for (i = n; i > 0; --i) {
  for (j = n; j > 0; --j) {
   count += 1;
  }
 }
}

var t = new Date();

setTimeout(function () {
 console.log(new Date() - t);
}, 1000);

heavyCompute(50000);

Copy after login
8520
Copy after login


As you can see, the actual execution time of the callback function that was supposed to be called after 1 second was greatly delayed because the JS main thread was busy running other code.

Code Design Patterns
Asynchronous programming has many unique code design patterns. In order to achieve the same function, the code written in synchronous mode and asynchronous mode will be very different. Some common patterns are introduced below.

Function return value
It is a very common requirement to use the output of one function as the input of another function. In synchronous mode, the code is generally written as follows:

var output = fn1(fn2('input'));
// Do something.
Copy after login

In asynchronous mode, since the function execution result is not passed through the return value, but through the callback function, the code is generally written in the following way:

fn2('input', function (output2) {
 fn1(output2, function (output1) {
  // Do something.
 });
});
Copy after login

As you can see, this method is one callback function nested within one callback function. If there are too many, it is easy to write >-shaped code.

Traverse the array
When traversing an array, it is also a common requirement to use a function to perform some processing on the data members in sequence. If the function is executed synchronously, the following code will generally be written:

var len = arr.length,
 i = 0;

for (; i < len; ++i) {
 arr[i] = sync(arr[i]);
}

// All array items have processed.

Copy after login

If the function is executed asynchronously, the above code cannot guarantee that all array members have been processed after the loop ends. If array members must be processed serially one after another, asynchronous code is generally written as follows:

(function next(i, len, callback) {
 if (i < len) {
  async(arr[i], function (value) {
   arr[i] = value;
   next(i + 1, len, callback);
  });
 } else {
  callback();
 }
}(0, arr.length, function () {
 // All array items have processed.
}));
Copy after login

As you can see, the above code only passes in the next array member and starts the next round of execution after the asynchronous function is executed once and returns the execution result. Until all array members are processed, the execution of subsequent code is triggered through callbacks. .

If array members can be processed in parallel, but subsequent code still requires all array members to be processed before they can be executed, the asynchronous code will be adjusted to the following form:

(function (i, len, count, callback) {
 for (; i < len; ++i) {
  (function (i) {
   async(arr[i], function (value) {
    arr[i] = value;
    if (++count === len) {
     callback();
    }
   });
  }(i));
 }
}(0, arr.length, 0, function () {
 // All array items have processed.
}));
Copy after login

As you can see, compared with the asynchronous serial traversal version, the above code processes all array members in parallel, and uses the counter variable to determine when all array members have been processed.

Exception handling
The exception catching and handling mechanism provided by JS itself - try..catch.., can only be used for synchronously executed code. Below is an example.

function sync(fn) {
 return fn();
}

try {
 sync(null);
 // Do something.
} catch (err) {
 console.log('Error: %s', err.message);
}

Copy after login
Error: object is not a function
Copy after login
Copy after login

As you can see, the exception will bubble along the code execution path until it is caught when it encounters the first try statement. However, since asynchronous functions interrupt the code execution path, when exceptions generated during and after the execution of the asynchronous function bubble up to the location where the execution path is interrupted, if no try statement is encountered, they will be thrown as a global exception. Below is an example.

function async(fn, callback) {
 // Code execution path breaks here.
 setTimeout(function () {
  callback(fn());
 }, 0);
}

try {
 async(null, function (data) {
  // Do something.
 });
} catch (err) {
 console.log('Error: %s', err.message);
}

Copy after login
/home/user/test.js:4
  callback(fn());
     ^
TypeError: object is not a function
 at null._onTimeout (/home/user/test.js:4:13)
 at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
Copy after login

因为代码执行路径被打断了,我们就需要在异常冒泡到断点之前用 try 语句把异常捕获住,并通过回调函数传递被捕获的异常。于是我们可以像下边这样改造上边的例子。

function async(fn, callback) {
 // Code execution path breaks here.
 setTimeout(function () {
  try {
   callback(null, fn());
  } catch (err) {
   callback(err);
  }
 }, 0);
}

async(null, function (err, data) {
 if (err) {
  console.log('Error: %s', err.message);
 } else {
  // Do something.
 }
});

Copy after login
Error: object is not a function
Copy after login
Copy after login

可以看到,异常再次被捕获住了。在 NodeJS 中,几乎所有异步 API 都按照以上方式设计,回调函数中第一个参数都是 err。因此我们在编写自己的异步函数时,也可以按照这种方式来处理异常,与 NodeJS 的设计风格保持一致。

有了异常处理方式后,我们接着可以想一想一般我们是怎么写代码的。基本上,我们的代码都是做一些事情,然后调用一个函数,然后再做一些事情,然后再调用一个函数,如此循环。如果我们写的是同步代码,只需要在代码入口点写一个 try 语句就能捕获所有冒泡上来的异常,示例如下。

function main() {
 // Do something.
 syncA();
 // Do something.
 syncB();
 // Do something.
 syncC();
}

try {
 main();
} catch (err) {
 // Deal with exception.
}

Copy after login

但是,如果我们写的是异步代码,就只有呵呵了。由于每次异步函数调用都会打断代码执行路径,只能通过回调函数来传递异常,于是我们就需要在每个回调函数里判断是否有异常发生,于是只用三次异步函数调用,就会产生下边这种代码。

function main(callback) {
 // Do something.
 asyncA(function (err, data) {
  if (err) {
   callback(err);
  } else {
   // Do something
   asyncB(function (err, data) {
    if (err) {
     callback(err);
    } else {
     // Do something
     asyncC(function (err, data) {
      if (err) {
       callback(err);
      } else {
       // Do something
       callback(null);
      }
     });
    }
   });
  }
 });
}

main(function (err) {
 if (err) {
  // Deal with exception.
 }
});

Copy after login

可以看到,回调函数已经让代码变得复杂了,而异步方式下对异常的处理更加剧了代码的复杂度。

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to use express to handle file upload in node project How to use express to handle file upload in node project Mar 28, 2023 pm 07:28 PM

How to handle file upload? The following article will introduce to you how to use express to handle file uploads in the node project. I hope it will be helpful to you!

An in-depth analysis of Node's process management tool 'pm2” An in-depth analysis of Node's process management tool 'pm2” Apr 03, 2023 pm 06:02 PM

This article will share with you Node's process management tool "pm2", and talk about why pm2 is needed, how to install and use pm2, I hope it will be helpful to everyone!

Pi Node Teaching: What is a Pi Node? How to install and set up Pi Node? Pi Node Teaching: What is a Pi Node? How to install and set up Pi Node? Mar 05, 2025 pm 05:57 PM

Detailed explanation and installation guide for PiNetwork nodes This article will introduce the PiNetwork ecosystem in detail - Pi nodes, a key role in the PiNetwork ecosystem, and provide complete steps for installation and configuration. After the launch of the PiNetwork blockchain test network, Pi nodes have become an important part of many pioneers actively participating in the testing, preparing for the upcoming main network release. If you don’t know PiNetwork yet, please refer to what is Picoin? What is the price for listing? Pi usage, mining and security analysis. What is PiNetwork? The PiNetwork project started in 2019 and owns its exclusive cryptocurrency Pi Coin. The project aims to create a one that everyone can participate

Token-based authentication with Angular and Node Token-based authentication with Angular and Node Sep 01, 2023 pm 02:01 PM

Authentication is one of the most important parts of any web application. This tutorial discusses token-based authentication systems and how they differ from traditional login systems. By the end of this tutorial, you will see a fully working demo written in Angular and Node.js. Traditional Authentication Systems Before moving on to token-based authentication systems, let’s take a look at traditional authentication systems. The user provides their username and password in the login form and clicks Login. After making the request, authenticate the user on the backend by querying the database. If the request is valid, a session is created using the user information obtained from the database, and the session information is returned in the response header so that the session ID is stored in the browser. Provides access to applications subject to

Quick Application: Practical Development Case Analysis of PHP Asynchronous HTTP Download of Multiple Files Quick Application: Practical Development Case Analysis of PHP Asynchronous HTTP Download of Multiple Files Sep 12, 2023 pm 01:15 PM

Quick Application: Practical Development Case Analysis of PHP Asynchronous HTTP Download of Multiple Files With the development of the Internet, the file download function has become one of the basic needs of many websites and applications. For scenarios where multiple files need to be downloaded at the same time, the traditional synchronous download method is often inefficient and time-consuming. For this reason, using PHP to download multiple files asynchronously over HTTP has become an increasingly common solution. This article will analyze in detail how to use PHP asynchronous HTTP through an actual development case.

How Swoole supports asynchronous SMTP operations How Swoole supports asynchronous SMTP operations Jun 25, 2023 pm 12:24 PM

With the continuous development and popularization of the Internet, email has become an indispensable part of people's lives and work, and SMTP (Simple Mail Transfer Protocol) is one of the important protocols for email sending. As an asynchronous network communication framework for PHP, Swoole can well support asynchronous SMTP operations, making email sending more efficient and stable. This article will introduce how Swoole supports asynchronous SMTP operations, including using sync

How Swoole supports asynchronous AMQP operations How Swoole supports asynchronous AMQP operations Jun 25, 2023 am 08:22 AM

As the volume of Internet business continues to grow, the demand for high concurrency and high performance is getting higher and higher, and Swoole, as a network communication framework for PHP, is increasingly favored by developers. Among them, Swoole supports asynchronous AMQP, which is one of the more common application scenarios. So let's take a look at how Swoole supports asynchronous AMQP operations. First, we need to clarify what AMQP is. AMQP (AdvancedMessageQueuingProtocol) Advanced

Advanced Guide to Python asyncio: From Beginner to Expert Advanced Guide to Python asyncio: From Beginner to Expert Mar 04, 2024 am 09:43 AM

Concurrent and Asynchronous Programming Concurrent programming deals with multiple tasks executing simultaneously, asynchronous programming is a type of concurrent programming in which tasks do not block threads. asyncio is a library for asynchronous programming in python, which allows programs to perform I/O operations without blocking the main thread. Event loop The core of asyncio is the event loop, which monitors I/O events and schedules corresponding tasks. When a coroutine is ready, the event loop executes it until it waits for I/O operations. It then pauses the coroutine and continues executing other coroutines. Coroutines Coroutines are functions that can pause and resume execution. The asyncdef keyword is used to create coroutines. The coroutine uses the await keyword to wait for the I/O operation to complete. The following basics of asyncio

See all articles