Node.js 异步编程方法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Copyright © 2022, 飞麦 <fitmap@qq.com>, All rights reserved.

// Node.js 异步编程方法示例
// async: 表明本函数为异步函数, 且此函数内部通常有 await 关键字
// await: 表明将执行权归还调度系统, 当调度系统察觉有空时继续执行,
//        并等待其后的表达式(通常为异步的)的最终执行结果
// 运行结果见: https://www.cnblogs.com/fitmap/p/16917452.html

// 各秒表的起始时刻字典
const map = new Map();
// 各异步程序的承诺数组
const promise_a = new Array(10);
// 一万个元素的数组
const rnd_a = new Array(10_000);

// 输出带有当前时刻的日志(对象)
function log(obj) {
    // 输出当前时刻及对象内容
    console.log(`${new Date().toISOString()} ${obj}`);
}

// 秒表用于展示各函数的执行时长, 以便观测异步函数的执行模式
// 启动秒表(秒表名)
function start(name) {
    // 输出秒表名
    log(`Start ${name}`);
    // 获取高精度时钟的当前时刻[毫秒], 并记录到本秒表的启动时刻中
    map.set(name, performance.now());
}

// 停止秒表(秒表名, 结果)
function stop(name, result = null) {
    // 获取高精度时钟的当前时刻[毫秒]
    const cur_time = performance.now();
    // 获取本秒表的启动时刻[毫秒]
    const old_time = map.get(name);
    // 计算差值[毫秒]{当前高精度时钟的最小刻度为100纳秒}
    const ms = Math.round((cur_time - old_time) * 10_000) / 10_000;
    result = result === null ? '' : ` result=${result}`;
    // 输出秒表名及毫秒数
    log(`Stop  ${name} ${ms}ms${result}`);
}

// 统计重复数值(排序后的随机数组)
function count_dup() {
    // 重复数值计数
    let dup_num = 0;
    // 前一个元素
    let old = null;
    // 对排序后的随机数组中的每个元素
    for (const ele of rnd_a) {
        // 如果当前元素的与前一个元素相等
        if (ele == old) {
            // 重复数值计数加一
            dup_num++;
        } else {
            // 将前一个元素设置为当前元素
            old = ele;
        }
    }
    return dup_num;
}

// 需要较长时间的异步函数(模式, 索引)
async function busy(model, idx) {
    // 本异步函数立即返回, 将程序的调度权归还给调度系统
    await null;
    // 调度系统空闲时继续执行下列语句
    // 确定秒表名
    const tname = `busy_${model}_${idx}`;
    // 启动复杂计算秒表
    start(tname);
    // 将数组的所有元素填充为随机整数
    for (let idx = 0; idx < rnd_a.length; idx++) {
        rnd_a[idx] = Math.round(Math.random() * rnd_a.length);
    }
    // 对数组进行排序
    rnd_a.sort();
    // 统计数组中的重复数值计数
    const result = count_dup();
    // 停止复杂计算秒表, 输出数组中的重复数值计数
    stop(tname, result);
    return result;
}

// 提交新承诺{系统有空就执行}(模式, 索引)
function new_promise(model, idx) {
    // 确定承诺秒表名
    const pname = `promise ${model}_${idx}`;
    // 启动提交承诺秒表
    start(pname);
    // 提交承诺{系统有空就执行}, 异步程序返回值被包装为 Promise 对象
    const promise = busy(model, idx);
    // 停止提交承诺秒表
    stop(pname);
    return promise;
}

// 准备阶段提交所有承诺{系统有空就执行}(模式)
function prepare(model) {
    // 启动准备秒表
    start('prepare');
    // 对承诺数组下标循环
    for (let idx = 0; idx < promise_a.length; idx++) {
        // 提交指定下标的承诺
        promise_a[idx] = new_promise(model, idx);
    }
    // 停止准备秒表
    stop('prepare');
}

// 执行各异步函数并统一等待()
async function unify() {
    // 启动统一等待秒表
    start('await unify');
    // 等待所有承诺完成并获得结果数组
    const result_a = await Promise.all(promise_a);
    // 停止统一等待秒表, 输出结果数组
    stop('await unify', result_a);
    return result_a;
}

// 执行各异步函数并逐个等待()
async function every() {
    // 启动全体等待秒表
    start('every');
    // 待返回的结果
    const result_a = [];
    // 对承诺数组的元素与下标循环
    for (const [idx, promise] of Object.entries(promise_a)) {
        // 确定单独秒表名
        const sname = `await every_${idx}`;
        // 启动单独等待秒表
        start(sname);
        // 等待当前承诺结果并放入结果数组
        result_a.push(await promise);
        // 停止单独等待秒表
        stop(sname);
    }
    // 停止全体等待秒表, 输出结果数组
    stop('every', result_a);
    return result_a;
}

// 主程序()
async function main() {
    // 启动主程序秒表
    start('main');
    // 提交一批承诺[保存在 promise_a 各元素中]
    prepare('every');
    // 启动提交 every 承诺秒表
    start('promise every');
    // 执行各异步函数并逐个等待, 提交 every_p 承诺[先执行]
    every_p = every();
    // 停止提交 every 承诺秒表
    stop('promise every');
    // 新提交一批承诺[注意原 promise_a 各元素未执行完因此仍存在]
    prepare('unify');
    // 启动提交 unify 承诺秒表
    start('promise unify');
    // 执行各异步函数并统一等待, 提交 unify_p 承诺[后执行]
    unify_p = unify();
    // 停止提交 unify 承诺秒表
    stop('promise unify');
    // 启动双承诺兑现秒表
    start('await dual');
    // 等待 every 和 unify 承诺兑现[先收集到 unify 兑现]
    const every_a_unify_a = await Promise.all([every_p, unify_p]);
    // 停止双承诺兑现秒表
    stop('await dual');
    // 拆解双承诺返回值
    const [every_a, unify_a] = every_a_unify_a;
    // 输出 every 承诺返回值
    log(`every_a=${every_a}`);
    // 输出 unify 承诺返回值
    log(`unify_a=${unify_a}`);
    // 停止主程序秒表
    stop('main');
    return every_a_unify_a;
}

main();
posted @ 2022-11-22 14:25  飞麦  阅读(62)  评论(0编辑  收藏  举报