Advanced usage of ES6 modular and asynchronous programming

target

  • Be able to know how to use the modular syntax of ES6
  • Can you know how to use Promise to solve the problem of callback hell
  • Know how to use async/await to simplify Promise calls
  • Be able to say what EventLoop is
  • Be able to tell the execution order of macro tasks and micro tasks

1, ES6 modularization

1. Review: how to implement modularity in node.js

node.js follows the modular specification of CommonJS. Of which:

  • Import other modules using the require() method
  • The module.exports object is used by the external shared members of the module

Benefits of modularity:

We all follow the same modular specification to write code, which reduces the cost of communication and greatly facilitates the mutual call between various modules for the benefit of others and ourselves.

2. Classification of front-end modular specifications

Before the birth of ES6 modular specification, the JavaScript community has tried and proposed AMD, CMD, CommonJS and other modular specifications.

However, these modular standards proposed by the community still have some differences and limitations, and are not general modular standards for browsers and servers, such as:

  • AMD and CMD are suitable for browser side Javascript modularization
  • CommonJS is suitable for server-side Javascript modularization

Too many modular specifications increase the learning difficulty and development cost for developers. Therefore, the unified ES6 modular specification was born!

3. What is the ES6 modular specification

ES6 modular specification is a general modular development specification for browser and server. Its appearance greatly reduces the modular learning cost of front-end developers, and developers do not need to learn AMD, CMD or CommonJS and other modular specifications.

Defined in ES6 modular specification:

  • Each js file is a separate module
  • Use the import keyword to import other module members
  • The export keyword is used by members of the outward shared module

4. Experience ES6 modularity in node.js

Node.js only supports CommonJS modular specification by default. If you want to experience and learn the modular syntax of ES6 based on node.js, you can configure it according to the following two steps:
① Ensure that v14.15.1 or later node.js is installed
② Add the "type": "module" node in the root node of package.json

Quick initialization package management profile:

$ npm init -y

{
 // -->Manually specify the type as module
  "type": "module",
  "name": "node_study",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

5. Basic syntax of ES6 modularization

The modularization of ES6 mainly includes the following three uses:
① Default export and default import
② On demand export and on demand import
③ Directly import and execute the code in the module

5.1 default export and default import

5.1.1 default export

Default export syntax: export default is the default exported member

01_m1.js:

5.1.2 default import

Default import syntax: import receive name from 'module identifier'

01_m2.js:

Execution:

5.1.3 precautions for default export

In each module, only one unique export default is allowed, otherwise an error will be reported!


Execution:

5.1.4 precautions for default import

By default, the receiving name during import can be any name, as long as it is a legal member name:

5.2 export on demand and import on demand

5.2.1 export on demand

Syntax of on demand export: export members exported on demand

5.2.2 import on demand

Syntax of on-demand import: import {S1} from 'module identifier'

5.2.3 precautions for on-demand export and on-demand import

① Multiple on-demand exports can be used in each module
② The name of the member imported on demand must be consistent with the name exported on demand
③ When importing on demand, you can rename using the as keyword
④ On demand import can be used with default import

02_m1.js:

export let s1 = "one"
export let s2 = "fine"

export function say() {
    console.log("Hello world!")
    return true
}

export default {
    a: 20
}

02_m2.js:

import info, { s1, s2 as str2, say } from './02_m1.js'

console.log(s1)
console.log(str2)
console.log(say())

console.log(info.a)

Execution:

5.3 directly import and execute the code in the module

If you only want to execute the code in a module, you don't need to get the shared members in the module. At this time, you can directly import and execute the module code. The example code is as follows:

03_m1.js:

// Perform a for loop operation in the current module
for (let i = 0; i < 3; i++) {
    console.log(i)
}

03_m2.js:

// You can import and execute module code directly without getting members shared by the module
import './03_m1.js'

Execution:

2, Promise

1. Callback hell

The nesting of multi-layer callback functions forms a callback hell. The example code is as follows:


Disadvantages of callback:

  • The code coupling is too strong, which affects the whole body and is difficult to maintain
  • A large number of redundant codes are nested with each other, and the readability of the code becomes poor

1.1 how to solve the problem of callback hell

In order to solve the problem of callback hell, the concept of Promise is added in ES6 (ECMAScript 2015).

1.2 basic concept of promise

① Promise is a constructor

  • We can create an instance of Promise const p = new Promise()
  • The Promise instance object from new represents an asynchronous operation

② Promise.prototype contains a. then() method

  • Each time, the instance object obtained by the new Promise() constructor,
  • You can access the. then() method through the prototype chain, such as p.then()

③ The. then() method is used to specify success and failure callback functions in advance

  • p. Then (successful callback function, failed callback function)
  • p.then(result => { }, error => { })
  • When calling the. then() method, the successful callback function is required and the failed callback function is optional

2. Read the file contents in order based on the callback function

3. Read file content based on then FS

Because the fs module officially provided by node.js only supports reading files in the way of callback functions, it does not support the call mode of Promise. Therefore, you need to run the following command to install then fs, a third-party package, to support us to read the contents of the file based on Promise:

$ npm install then-fs

3.1 basic use of then FS

Call the readFile() method provided by then fs to asynchronously read the contents of the file, and its return value is the Promise instance object. Therefore, you can call the. then() method to specify the callback function after success and failure for each Promise asynchronous operation. The example code is as follows:

Note: the above code can not guarantee the reading order of the file, so it needs to be further improved!

3.2 characteristics of the. Then() method

If a new Promise instance object is returned in the previous. then() method, processing can continue through the next. then(). Through the chain call of the. then() method, the problem of callback hell is solved.

3.3 read the contents of files in order based on Promise

Promise supports chained calls to solve the problem of callback hell. The example code is as follows:

3.4 catching errors through. catch

If an error occurs in Promise's chain operation, you can use Promise.prototype.catch method to capture and process it:

If you don't want the previous error to cause the subsequent. then to fail to execute normally, you can call. catch in advance. The example code is as follows:

3.5 Promise.all() method

Promise.all() method will initiate parallel promise asynchronous operations. After all asynchronous operations are completed, the next. then operation (waiting mechanism) will be executed. The example code is as follows:

Note: the order of Promise instances in the array is the order of the final results!

3.6 Promise.race() method

Promise.race() method will initiate parallel promise asynchronous operation. As long as any asynchronous operation is completed, the next. then operation (race mechanism) will be executed immediately. The example code is as follows:

4. File reading method based on Promise encapsulation

Packaging requirements of the method:
① The name of the method is defined as getFile
② Method receives a formal parameter fpath representing the path of the file to be read
③ The return value of the method is Promise instance object

4.1 basic definition of GetFile method

Note: new Promise() in line 5 just creates a formal asynchronous operation.

4.2 create specific asynchronous operations

If you want to create a specific asynchronous operation, you need to pass a function function during the new Promise() constructor to define the specific asynchronous operation inside the function function function. The example code is as follows:

4.3 get two arguments of. then

The successful and failed callback functions specified by. then() can be received in the formal parameters of function. The example code is as follows:

4.4 calling the resolve and reject callback functions

The result of Promise asynchronous operation can be processed by calling the resolve or reject callback function. The example code is as follows:

04_m.js:

import fs from 'fs'

function getFile(fpath) {
    return new Promise(function (resolve, reject) {
        fs.readFile(fpath, 'utf8', (err, dataStr) => {
            if (err)
                return reject(err)
            resolve(dataStr)
        })
    })
}

getFile('./files/1.txt').then(
    r => console.log(r),
    err => console.log(err.message)
)

3, async/await

1. What is async/await

async/await is a new syntax introduced by ES8 (ECMAScript 2017) to simplify Promise asynchronous operations. Before async/await, developers could only handle Promise asynchronous operations by chaining. then(). The example code is as follows:

  • Advantages of. then chained call: it solves the problem of callback hell
  • Disadvantages of. then chain call: code redundancy, poor readability and difficult to understand

2. Basic use of async / await

The sample code of using async/await to simplify Promise asynchronous operation is as follows:

Note: await acts before the method of the object whose return value is Promise.

3. Precautions for async / await

① If await is used in function, the function must be modified by async
② In the async method, the code before the first await will be executed synchronously, and the code after the first await will be executed asynchronously


4, EventLoop

1. JavaScript is a single threaded language

JavaScript is a single threaded programming language. In other words, you can only do one thing at a time.

Problems with single thread execution task queue:

If the previous task is very time-consuming, the subsequent tasks have to wait all the time, resulting in the problem of fake death of the program.

2. Synchronous and asynchronous tasks

In order to prevent a time-consuming task from causing the program to fake death, JavaScript divides the tasks to be executed into two categories:

① synchronous task

  • Also known as non time consuming tasks, these tasks are those queued on the main thread
  • The next task can only be executed after the previous task has been executed

② asynchronous task

  • Also known as time-consuming tasks, asynchronous tasks are delegated by JavaScript to the host environment for execution
  • When the asynchronous task is completed, the JavaScript main thread will be notified to execute the callback function of the asynchronous task

3. Execution process of synchronous task and asynchronous task

① Synchronization tasks are executed sequentially by the JavaScript main thread
② Asynchronous tasks are delegated to the host environment for execution
③ The callback function corresponding to the completed asynchronous task will be added to the task queue for execution
④ After the execution stack of the JavaScript main thread is cleared, the callback functions in the task queue will be read and executed in order
⑤ The JavaScript main thread repeats step 4 above

4. Basic concept of EventLoop

The JavaScript main thread reads the callback function of asynchronous tasks from the "task queue" and puts it into the execution stack for execution in turn. This process is endless, so the whole operation mechanism is also called EventLoop.

5. Analyze the output sequence in combination with EventLoop

Correct output: ADCB. Of which:

  • A and D are synchronous tasks. Will be executed according to the order of the code
  • C and B are asynchronous tasks. Their callback functions will be added to the task queue and executed when the main thread is idle

5, Macro and micro tasks

1. What are macro tasks and micro tasks

JavaScript further divides asynchronous tasks into two categories:

① Macro task

  • Asynchronous Ajax requests
  • setTimeout,setInterval
  • File operation
  • Other macro tasks

② microtask

  • Promise.then,. catch and. finally
  • process.nextTick
  • Other micro tasks

2. Execution sequence of macro task and micro task

After each macro task is executed, it will check whether there are micro tasks to be executed. If so, it will continue to execute the next macro task after all micro tasks are executed.

3. Scenario of going to the bank to do business

① Xiaoyun and xiaoteng go to the bank to do business. First, you need to queue up after taking the number

  • Macro task queue

② Assuming that there is only one teller in the current bank outlet, xiaoteng can only wait when Xiaoyun handles the deposit business

  • Single thread, macro tasks are executed in order

③ After Xiaoyun finished his deposit business, the teller asked him if he still wanted to handle other businesses?

  • After the current macro task is executed, check whether there are micro tasks

④ Xiao Yun told the teller: do you want to buy financial products, apply for a credit card, and finally exchange some commemorative coins for the year of the horse?

  • The micro task is executed, and the subsequent macro task is postponed

⑤ After Xiaoyun left the counter, the teller began to handle business for xiaoteng

  • After all micro tasks are executed, start to execute the next macro task

4. Analyze the order of the following code output

Note: after new Promise obtains the Promise instance, the function inside will be executed immediately. Here, function is a synchronization task

The correct output sequence is 2431; analysis:

① Perform all synchronization tasks first

  • Execute lines 6 and 12

② Then execute the micro task

  • Execute line 9

③ Perform the next macro task

  • Execute line 2 code

5. Classic interview questions

Please analyze the order of the following code output:

console.log('1')  // Synchronization task

setTimeout(function () {  // Macro task
    console.log('2')

    new Promise(function (resolve) {
        console.log('3')
        resolve()
    }).then(function () {
        console.log('4')
    })
})

new Promise(function (resolve) {  // Synchronization task
    console.log('5')
    resolve()
}).then(function () {  // Micro task
    console.log('6')
})


setTimeout(function () {  // Macro task
    console.log('7')

    new Promise(function (resolve) {
        console.log('8')
        resolve()
    }).then(function () {
        console.log('9')
    })
})

The correct output order is: 15 6 234 789

6, API interface cases

1. Case requirements

Based on MySQL database + Express, it provides API interface service for external user list. The technical points used are as follows:

  • Third party packages express and mysql2
  • ES6 modularization
  • Promise
  • async/await

2. Main implementation steps

① Build the basic structure of the project

② Create basic server

③ Create db database operation module

④ Create user_ctrl service module

⑤ Create user_router routing module

3. Build the basic structure of the project

① Enable ES6 modular support

  • Declare "type" in package.json: "module"

② Installing third-party dependency packages

  • Run NPM install express@4.17.1 mysql2@2.2.5

package.json:

{
  "type": "module",
  "name": "demo-code",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "mysql2": "^2.2.5"
  }
}

4. Create a basic server

app.js:

// Use the default import syntax of ES6
import express from 'express'
const app = express()

app.listen(80, () => {
    console.log('server running at http://127.0.0.1 ...')
})

function:

demo-code> $ npm install -g nodemon
demo-code> $ nodemon app.js
[nodemon] 2.0.12
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node app.js`
server running at http://127.0.0.1 ...

5. Create db database operation module

db/index.js:

import mysql from 'mysql2'

const pool = mysql.createPool({
    host: '127.0.0.1',
    pool: '3306',
    database: 'my_db_01',  // The name of the database to operate on
    user: 'root',
    password: 'root',

})

// Export a pool that supports Promise API by default
export default pool.promise()

Attached:

/*
 Navicat Premium Data Transfer

 Source Server         : 127.0.0.1
 Source Server Type    : MySQL
 Source Server Version : 100504
 Source Host           : localhost:3306
 Source Schema         : my_db_01

 Target Server Type    : MySQL
 Target Server Version : 100504
 File Encoding         : 65001

 Date: 23/09/2021 00:19:31
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for ev_users
-- ----------------------------
DROP TABLE IF EXISTS `ev_users`;
CREATE TABLE `ev_users`  (
  `id` int NOT NULL,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of ev_users
-- ----------------------------
INSERT INTO `ev_users` VALUES (1, 'one', 'ione');
INSERT INTO `ev_users` VALUES (2, 'fine', 'ifine');
INSERT INTO `ev_users` VALUES (3, 'onefine', 'ionefine');

SET FOREIGN_KEY_CHECKS = 1;

6. Create user_ctrl module

controller/user_ctrl.js:

import db from '../db/index.js'

// Use the on-demand export syntax of ES6 to export methods
export async function getAllUser(req, res) {
    // The return value of the db.query() function is the instance object of Promise. Therefore, async/await can be used for simplification
    const [rows] = await db.query('select id, username, nickname from ev_users')
    // console.log(rows)

    res.send({
        status: 0,
        message: 'Get user list succeeded!',
        data: rows
    })
}

7. Create user_router module

router/user_router.js:

import express from 'express'
// From user_ Import the getAllUser function as needed in the ctrl.js module
import { getAllUser } from '../controller/user_ctrl.js'

// Create routing object
const router = new express.Router()

// Mount routing rules
router.get('/user', getAllUser)

// Use the default export syntax of ES6 to share routing objects
export default router

8. Import and mount the routing module

app.js:

// Use the default import syntax of ES6
import express from 'express'
// Import routing objects using default import
import userRouter from './router/user_router.js';

const app = express()

// Mount user routing module
app.use('/api', userRouter)

app.listen(80, () => {
    console.log('server running at http://127.0.0.1 ...')
})

function:

demo-code> $ nodemon app.js
[nodemon] 2.0.12
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node app.js`
server running at http://127.0.0.1 ...

9. Use try... Catch to catch exceptions

summary

① Be able to know how to use the modular syntax of ES6

  • Default export and default import, on-demand export and on-demand import

② Know how to use Promise to solve the callback problem

  • promise.then(),promise.catch()

③ Be able to use async/await to simplify the call of Promise

  • If await is used in the method, the method needs to be modified by async

④ Be able to say what EventLoop is

  • Schematic diagram of EventLoop

⑤ Be able to tell the execution order of macro tasks and micro tasks

  • Before executing the next macro task, check whether there are micro tasks to be executed

Tags: Javascript node.js ECMAScript Module

Posted on Wed, 22 Sep 2021 12:05:43 -0400 by KDragon