Synchronous and asynchronous
introduction
As Web applications continue to develop, JavaScript, as a widely used programming language, is also constantly developing and evolving. In Web front-end development, JavaScript is mainly used for browser UI processing. UI development is a typical single-threaded event-driven model, so JavaScript has also formed a programming paradigm with asynchronous processing as the main programming paradigm. However, in large-scale and complex applications, the problems and complexity caused by asynchronous programming are becoming more and more obvious.
The emergence of Node.js brings a new asynchronous programming paradigm to JavaScript: event loops and callback functions. This programming paradigm is efficient and concise, and is suitable for high-concurrency and I/O-intensive scenarios. However, this programming paradigm also brings its own problems and complexities. Especially in large-scale and complex applications, programmers need to deal with the problem of nesting many callback functions and need to deal with the problem of asynchronous call order, which increases the complexity and difficulty of the program.
In order to solve these problems and difficulties, fibjs came into being. fibjs is an application server development framework mainly designed for web back-end development. It is based on the Google v8 JavaScript engine and chooses a different concurrency solution than the traditional callback. fibjs uses fiber to isolate the business complexity caused by asynchronous calls at the framework layer, greatly reducing the difficulty of development and reducing performance problems caused by frequent asynchronous processing in user space. At the same time, compared with the traditional asynchronous programming paradigm, its synchronous programming paradigm has the advantages of more readability, simple logic, and easy maintenance.
In the following content, we will introduce the advantages and features of fibjs, and explain how to use its synchronous and asynchronous programming solutions through examples.
fiber introduction
fibjs is a high-performance JavaScript server framework based on the v8 engine, mainly for Web back-end development. Started in 2009, it already has high stability and productivity, and has a wide range of application cases at home and abroad.
In fibjs, fiber is used to solve the problem between business logic and I/O processing. Fiber is different from traditional concepts such as threads, coroutines, and processes. It is a user-level lightweight thread that can be regarded as a cooperative multi-tasking mechanism. Fiber can perform business logic and I/O operations in different contexts, and manages resources internally through pre-allocation and recycling. Compared with traditional threads and processes, it is more lightweight, more flexible, and more efficient. .
Compared with other thread libraries (such as pthread, WinThread, Boost.Thread, etc.), fiber has the following advantages:
Collaborative scheduling : Fiber is a collaborative scheduling that does not require preemptive scheduling by the kernel or operating system. It reduces frequent context switching, speeds up the running speed of the program, and avoids competition conditions and deadlock problems between threads.
Lightweight : Each fiber only consumes a small stack space, and a large number of fibers can be created in multi-concurrent applications without causing the problem of occupying too much memory.
Efficiency : fiber is implemented based on the characteristics of the JavaScript language itself, and makes full use of the superior performance of the v8 engine, which is faster than traditional thread libraries.
By using fiber, fibjs can separate business logic and I/O processing, thereby encapsulating asynchronous calls into the form of synchronous calls, making writing and maintaining code simpler and easier to read, and at the same time giving full play to the advantages of the JavaScript language.
Synchronous programming in fibjs
In asynchronous programming, the nesting of callback functions may lead to poor readability of the code, easily cause the problem of callback hell, and increase the difficulty of the code and the cost of debugging. The synchronous programming paradigm is more in line with human thinking patterns, making the code structure clearer, easier to read, and easier to maintain, which can greatly improve development efficiency and code quality.
In fibjs, synchronous programming is a very popular and commonly used programming paradigm, which makes the structure and logic of the code more intuitive, easy to understand and maintain. Some synchronous programming functions and modules are highly supported in fibjs, such as util.sync, fs.readSync, etc.
In fibjs, you can directly call asynchronous functions of built-in objects in a synchronous manner:
1
2
3
4const fs = require("fs");
const data = fs.readFile("/path/to/file");
console.log(data);
You can also wrap the asynchronous function through util.sync and try...catch, so that the fiber can obtain the return value of the asynchronous call, thereby achieving the synchronization effect, for example:
1
2
3
4
5
6
7
8
9
10
11
12
13// load module
const coroutine = require("coroutine");
const util = require("util");
const fs = require("fs");
// use util.sync to wrap fs.readFile
function readFile(path) {
return util.sync(fs.readFile)(path);
}
// call the sync function
const data = readFile("myfile.txt");
console.log(data);
In the above example, we defined a function named readFile and used util.sync to encapsulate the asynchronous fs.readFile function into a synchronous function. This function can directly return data through synchronous calls. This synchronous calling method is similar to the traditional JavaScript programming paradigm. The difference is that in fibjs, the thread is not blocked, but the asynchronous effect is achieved through fiber.
How util.sync works
util.sync is an efficient wrapper function of the kernel. The following JavaScript code can achieve similar functions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23const coroutine = require("coroutine");
function sync(func) {
return function _warp() {
var ev = new coroutine.Event();
var e, r;
func.apply(this, [
...arguments,
function (err, result) {
e = err;
r = result;
ev.set();
}
]);
ev.wait();
if (e)
throw e;
return r;
}
}
This code defines a tool function sync for converting an asynchronous callback function into a synchronous calling function. It receives a function func and returns a new function _wrap. This new function implements the function of converting the original function into a synchronous call. In the _wrap function, a new Event object ev is first created for thread scheduling and waiting for asynchronous callback results. Then use the apply method to call the original function func with the specified parameters and a new callback function as parameters. During the calling process, an asynchronous callback occurs, and the new callback function stores the returned results into variables e and r, and wakes up the Event object. Finally, it is decided according to the variable e whether to throw an exception or return the variable r. This function implements a solution for converting asynchronous callback functions into synchronous calls, which can improve the readability and maintainability of the function.
Asynchronous programming in fibjs
In fibjs, most asynchronous methods (including I/O and network request methods, etc.) can support both synchronous and asynchronous calls, which allows developers to choose which method to use according to their programming needs at any time.
Taking fs.readFile() as an example, we can use this method in two ways:
Asynchronous method: Process the result of reading the file by passing a callback function, for example:
1
2
3
4
5
6const fs = require("fs");
fs.readFile("/path/to/file", (err, data) => {
if (err) throw err;
console.log(data);
});
This method is suitable for situations where you need to perform some operations after reading the file.
Synchronous method: Get the contents of the file by not passing a callback function, for example:
1
2
3
4const fs = require("fs");
const data = fs.readFile("/path/to/file");
console.log(data);
In this example, we obtain the contents of the file by reading the return value data of the file. There is no need to wait for the callback function to complete the file reading before continuing the operation. This method is suitable for situations where you need to perform some operations before reading the file is completed.
It can be seen that this feature of supporting both synchronous and asynchronous calls allows developers to choose to use different methods according to their own needs and development scenarios. In some cases, synchronous code is more readable and easier to maintain and debug; while in some cases, asynchronous code can better improve the response speed and performance of the code.
However, you need to be careful when using the synchronization method. In some scenarios, this method may block the current fiber. Therefore, we need to choose the appropriate programming method based on actual needs.
in conclusion
In this article, we introduce the synchronous programming style and asynchronous programming solutions of fibjs as well as their advantages and application scenarios. We mentioned that fibjs can reduce operational complexity and improve code development efficiency by using fiber to isolate business logic and performance issues caused by asynchronous processing. At the same time, we also emphasized the advantages of fibjs in I/O processing and memory management, which brings great convenience to development, testing and maintenance.
Finally, we encourage readers to explore fibjs in depth and participate in fibjs contributions and community activities. We believe that fibjs will continue to attract the attention and support of the open source community with its powerful performance and ease of use.