This article mainly introduces the usage of understanding javascript async. The editor thinks it is quite good. Now I will share it with you and give it as a reference. Let’s follow the editor to take a look
Written in front
This article will implement an optimal method for sequentially reading files. The implementation method starts from the oldest The callback method to the current async, I will also share with you my understanding of the thunk library and the co library. The effect achieved: read a.txt and b.txt sequentially, and concatenate the read contents into a string.
Synchronous reading
const readTwoFile = () => { const f1 = fs.readFileSync('./a.txt'), f2 = fs.readFileSync('./b.txt'); return Buffer.concat([f1, f2]).toString(); };
This method is the most conducive to our understanding, the code is also very clear, without too much The nesting is very easy to maintain, but this has the biggest problem, that is, performance. What node advocates is asynchronous I/O to handle intensive I/O, and synchronous reading is wasteful to a large extent. Server CPU, the disadvantages of this method obviously outweigh the advantages, so just pass it. (In fact, the goal of any asynchronous programming solution in node is to achieve synchronous semantics and asynchronous execution.)
Use callbacks to read
const readTwoFile = () => { let str = null; fs.readFile('./a.txt', (err, data) => { if (err) throw new Error(err); str = data; fs.readFile('./b.txt', (err, data) => { if (err) throw new Error(err); str = Buffer.concat([str, data]).toString(); }); }); };
Using the callback method, it is very simple to implement. Just nest it directly. However, in this case, it is easy to cause a situation that is difficult to maintain and difficult to understand. The most extreme The situation is callback hell.
Promise implementation
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const readTwoFile = () => { let bf = null; readFile('./a.txt') .then( data => { bf = data; return readFile('./b.txt'); }, err => { throw new Error(err) } ) .then( data => { console.log(Buffer.concat([bf, data]).toString()) }, err => { throw new Error(err) } ); };
Promise can convert horizontal growth callbacks into vertical growth, which can solve some problems. But the problem caused by Promise is code redundancy. At first glance, it is all then, which is not very comfortable, but compared with callback function nesting, it has been greatly improved.
yield
Generator is found in many languages. It is essentially a coroutine. Let’s take a look at the differences and connections between coroutines, threads, and processes. :
Process: The basic unit of resource allocation in the operating system
Thread: The basic unit of resource scheduling in the operating system
Coroutine: an execution unit smaller than a thread, with its own CPU context, one coroutine and one stack
There may be multiple threads in a process. There may be multiple coroutines in a thread. The switching of processes and threads is controlled by the operating system, while the switching of coroutines is controlled by the programmer himself. Asynchronous I/O uses callbacks to deal with intensive I/O. You can also use coroutines to deal with it. Switching coroutines does not waste a lot of resources. Write an I/O operation into a coroutine, and proceed like this During I/O, you can give up the CPU to other coroutines.
js also supports coroutines, which is yield. The intuitive feeling that using yield gives us is that the execution stops at this place and other code continues to run. When you want it to continue execution, it will continue to execute.
function *readTwoFile() { const f1 = yield readFile('./a.txt'); const f2 = yield readFile('./b.txt'); return Buffer.concat([f1, f2]).toString(); }
The sequential reading under yield is also a sequential reading method. There are two different implementation methods for readFile,
Use thunkify
const thunkify = (fn, ctx) => (...items) => (done) => { ctx = ctx || null; let called = false; items.push((...args) => { if (called) return void 0; called = true; done.apply(ctx, args); }); try { fn.apply(ctx, items); } catch(err) { done(err); } };
The thunkify function is a kind of currying idea. The last parameter passed in is the callback function. It can be easily done using thunkify. Implement the automated process of yield function:
const run = fn => { const gen = fn(); let res; (function next(err, data) { let g = gen.next(data); if (g.done) return void 0; g.value(next); })(); };
Use Promise
##
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const run = fn => { const gen = fn(); let str = null; (function next(err, data) { let res = gen.next(data); if (res.done) return void 0; res.value.then( data => { next(null, data); }, err => { throw new Error(err); } ); })(); }; run(readTwoFile);Both of the above methods are available To achieve the process of automatically executing yield, is there a way that is compatible with these two implementation methods? Master TJ has given another library, which is the co library. Let’s take a look at the usage first:
// readTwoFile的实现与上面类似,readFile既可以利用Promise也可以利用thunkify // co库返回一个Promise对象 co(readTwoFile).then(data => console.log(data));Let’s take a look at the implementation of the co library. The co library will return a Promise object by default. For the value after yield (such as res.value above), the co library will convert it into a Promise. The implementation idea is very simple, basically using recursion. The general idea is as follows:
const baseHandle = handle => res => { let ret; try { ret = gen[handle](res); } catch(e) { reject(e); } next(ret); }; function co(gen) { const ctx = this, args = Array.prototype.slice.call(arguments, 1); return new Promise((reslove, reject) => { if (typeof gen === 'function') gen = gen.apply(ctx, args); if (!gen || typeof gen.next !== 'function') return resolve(gen); const onFulfilled = baseHandle('next'), onRejected = baseHandle('throw'); onFulfilled(); function next(ret) { if (ret.done) reslove(ret.value); // 将yield的返回值转换为Proimse const value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected(new TypeError('yield type error')); } }); }toPromise is to convert some types into Promise. From here we can see that Which types can be placed behind yield, here is a commonly used one:
// 把thunkify之后的函数转化为Promise的形式 function thunkToPromise(fn) { const ctx = this; return new Promise(function (resolve, reject) { fn.call(ctx, function (err, res) { if (err) return reject(err); if (arguments.length > 2) res = slice.call(arguments, 1); resolve(res); }); }); }Recently, Node has supported async/await, which can be used to perform asynchronous operations:
Ultimate Solution
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const readTwoFile = async function() { const f1 = await readFile('./a.txt'); const f2 = await readFile('./b.txt'); return Buffer.concat([f1, f2]).toString(); }; readTwoFile().then(data => { console.log(data); });What async/await does is to connect Promise objects in series to avoid then The calling method, the code is very easy to read, and it is a synchronous method. You no longer need to rely on other external class libraries (such as co libraries) to elegantly solve the callback problem
The above is the detailed content of Detailed explanation of the usage of async in javascript. For more information, please follow other related articles on the PHP Chinese website!

JavaScript's application in the real world includes front-end and back-end development. 1) Display front-end applications by building a TODO list application, involving DOM operations and event processing. 2) Build RESTfulAPI through Node.js and Express to demonstrate back-end applications.

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

Understanding how JavaScript engine works internally is important to developers because it helps write more efficient code and understand performance bottlenecks and optimization strategies. 1) The engine's workflow includes three stages: parsing, compiling and execution; 2) During the execution process, the engine will perform dynamic optimization, such as inline cache and hidden classes; 3) Best practices include avoiding global variables, optimizing loops, using const and lets, and avoiding excessive use of closures.

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

Python and JavaScript have their own advantages and disadvantages in terms of community, libraries and resources. 1) The Python community is friendly and suitable for beginners, but the front-end development resources are not as rich as JavaScript. 2) Python is powerful in data science and machine learning libraries, while JavaScript is better in front-end development libraries and frameworks. 3) Both have rich learning resources, but Python is suitable for starting with official documents, while JavaScript is better with MDNWebDocs. The choice should be based on project needs and personal interests.

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

JavaScript's applications in the real world include server-side programming, mobile application development and Internet of Things control: 1. Server-side programming is realized through Node.js, suitable for high concurrent request processing. 2. Mobile application development is carried out through ReactNative and supports cross-platform deployment. 3. Used for IoT device control through Johnny-Five library, suitable for hardware interaction.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

SublimeText3 English version
Recommended: Win version, supports code prompts!

Dreamweaver Mac version
Visual web development tools

Zend Studio 13.0.1
Powerful PHP integrated development environment

SublimeText3 Mac version
God-level code editing software (SublimeText3)

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function