Promise解决回调地狱

undefined

最近第一次将自己写的代码交付给别人。“老哥,这这是标准的callback hell啊”“老哥,你这是幽灵代码吧…”好吧,第一次得到的评价还不错:) 那么既然遇到问题了,当然是解决它啊。
Promise对象
Generator函数

首先,先拜读上面两则文档,它们是解决异步回调的一种解决方案。我说说我的粗糙理解吧。使用它们会让你的异步回调看起来更加好看,更加易操纵,更加易维护。不至于让异步操作层层嵌套,横向发展。

大部分操作都在里面可以找到答案,所以我这里也不再多废话。直接贴上我一个实际运用的Generator例子。以供参考。

最近利用Sequelize模块多写了几次的异步操作。感觉体会更深了一点。首先如果不使用async函数来实现,那么务必引入co模块来使用promise对象。(可以免去很多繁杂的代码构成)

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
'use strict';
var query = require('../model/mysql.js');
const csvParser = require('csv-parser');
const fs = require('fs');
const co = require('co');
const DESC = {
empty: '暂无更新',
updating: '更新中,请等待一段时候后重新进入此页面',
success: `更新成功`,
fail: '更新失败',
};
// 标记服务器启动后最后一次更新的状态
let tips = '暂无更新';
exports.updatePage = function(req, res) {
res.render('teacherinfo', { tips });
}
// 封装 Promise 供update使用
function moveFile(file, path) {
return new Promise((resolve, reject) => {
file.mv(path, function(err) {
if(err)
reject(new Error('移动文件失败'));
else
resolve();
});
});
}
function asyncQuery(sql, errorMsg) {
return new Promise((resolve, reject) => {
query(sql, (err, result) => {
if(err)
reject((errorMsg === null || errorMsg === undefined) ? err : new Error(errorMsg));
else
resolve(result);
});
});
}
exports.update = function (req, res) {
// 使用 co 模块作为 generator 的执行器
co(function* () {
const file = req.files.file;
if(file === null || file === undefined) {
tips = `${DESC.fail} 没有文件`;
res.redirect('/teacherinfo');
return;
}
tips = `${DESC.updating}`;
res.redirect('/teacherinfo');
const path = './teacherinfo/teacher.txt';
// 先进行文件的移动,再进行数据表的清空,这两步完成后才可进行后续的操作
yield moveFile(file, path);
yield asyncQuery('TRUNCATE swzx_teacher_info', '清空数据表失败');
const parser = csvParser({
separator: '\t'
});
let insertCount = 0;
const now = new Date();
const timeString = `${now.getFullYear()}年${now.getMonth()}月${now.getDate()}日 ${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
const stream = fs.createReadStream(path).pipe(parser);
stream.on('data', row => {
const sql = `INSERT INTO swzx_teacher_info (name, unit, time, office, phone, email) VALUES ('${row.name}', '${row.unit}', '${row.time}', '${row.office}', '${row.phone}', '${row.email}')`;
query(sql, (err, result) => {
if(err) {
console.log('teacher info insert fail.');
console.log(err);
tips = `${DESC.fail} 增加数据失败`;
return;
}
insertCount++;
tips = `${DESC.success} 于${timeString}更新了${insertCount}条数据`;
});
});
}).catch((err) => { // co 返回一个 Promise 对象
tips = `${DESC.fail} ${err.message}`;
});
}
// 服务器上跑的代码
exports.update = function(req, res) {
const file = req.files.file;
if(file === null || file === undefined) {
tips = `${DESC.fail} 没有文件`;
res.end();
return;
}
tips = `${DESC.updating}`;
res.redirect('/teacherinfo');
// 放置在应用根目录下的teacherinfo目录下,不放置在public文件夹中防止信息被获取
const filePath = `./teacherinfo/teacher.txt`;
file.mv(filePath, function(err) {
if(err) {
console.log(err);
tips = `${DESC.fail} 移动失败`;
res.end();
return;
}
query('TRUNCATE swzx_teacher_info', (err, result) => {
if(err) {
console.log(err);
tips = `${DESC.fail} 清空数据表失败`;
res.end();
return;
}
const parser = csvParser({
separator: '\t'
});
let insertCount = 0;
const now = new Date();
const timeString = `${now.getFullYear()}年${now.getMonth()}月${now.getDate()}日 ${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
const stream = fs.createReadStream(filePath).pipe(parser);
stream.on('data', data => {
query(`INSERT INTO swzx_teacher_info (name, unit, time, office, phone, email) VALUES ('${data.name}', '${data.unit}', '${data.time}', '${data.office}', '${data.phone}', '${data.email}')`, (err, result) => {
if(err) {
console.log('teacher info insert fail.');
console.log(err);
tips = `${DESC.fail} 增加数据失败`;
return;
}
insertCount++;
tips = `${DESC.success} 于${timeString}更新了${insertCount}条数据`;
});
})
});
});
}