Nodejs:请用async 或 Q 优化以下的蚯蚓 | 金字塔 | callback hell
Nodejs:请用async 或 Q 优化以下的蚯蚓 | 金字塔 | callback hell
var temp = [];
for(var i=0; i <= data.length; i++) {
temp.push({
id : data[i].id,
chk: data[i].chk === true ? '1' : '2'
});
for(var j=0; j <= data[i].children.length; j++) {
temp.push({
id : data[i].children[j].id,
chk: data[i].children[j].chk === true ? '1' : '2'
});
for(var k=0; k <= data[i].children[j].children.length; k++) {
temp.push({
id : data[i].children[j].children[k].id,
chk: data[i].children[j].children[k].chk === true ? '1' : '2'
});
}
}
}
Node.js: 请用 async 或 Q 优化以下的蚯蚓 | 金字塔 | callback hell
在Node.js中处理嵌套的回调(也称为"callback hell")时,使用async
库或Q
库可以显著提高代码的可读性和维护性。下面我将分别展示如何使用这两个库来优化你的代码。
使用 async
库
async
是一个非常流行的库,用于管理异步操作。它提供了诸如 each
, forEach
, map
等方法,可以帮助我们避免深层嵌套的回调。
const async = require('async');
let temp = [];
async.each(data, (item, callback) => {
temp.push({
id: item.id,
chk: item.chk ? '1' : '2'
});
async.each(item.children, (child, childCallback) => {
temp.push({
id: child.id,
chk: child.chk ? '1' : '2'
});
async.each(child.children, (grandChild, grandChildCallback) => {
temp.push({
id: grandChild.id,
chk: grandChild.chk ? '1' : '2'
});
grandChildCallback();
}, childCallback);
}, callback);
}, err => {
if (err) throw err;
console.log(temp);
});
使用 Q
库
Q
是另一个强大的Promise库,可以用来管理异步操作。Promise使得异步代码更易于阅读和维护。
const Q = require('q');
const asyncMap = Q.nfbind(require('async').map);
let promises = data.map(item => {
return Q()
.then(() => {
temp.push({
id: item.id,
chk: item.chk ? '1' : '2'
});
})
.then(() => {
return asyncMap(item.children, (child, mapCallback) => {
temp.push({
id: child.id,
chk: child.chk ? '1' : '2'
});
mapCallback(null);
});
})
.then(() => {
return asyncMap(item.children, (child, mapCallback) => {
return asyncMap(child.children, (grandChild, grandChildCallback) => {
temp.push({
id: grandChild.id,
chk: grandChild.chk ? '1' : '2'
});
grandChildCallback(null);
}, mapCallback);
});
});
});
Q.all(promises).then(() => {
console.log(temp);
}).catch(err => {
throw err;
});
解释
-
使用
async
:async.each
方法用于遍历数组中的每个元素,并对每个元素执行一个函数。- 每个内部循环都通过调用外部循环的回调来完成,从而保持顺序。
-
使用
Q
:Q()
创建一个新的Promise对象。Q.nfbind
将Node.js风格的回调函数转换为返回Promise的函数。asyncMap
使用Q.nfbind
转换后的函数来处理数组。Q.all
确保所有异步操作完成后才输出结果。
这两种方法都能有效地减少代码的嵌套深度,使代码更加清晰和易于理解。
现在也就我这样的闲人来算算了
####工具util.js
// 提取流数据
function dataParse (stream, f) {
var result = '';
// 提取流数据块
stream.on('data', 'utf8', function (data) {
result += data;
});
// 通过f映射合并后的数据
stream.on('end', function () {
f(result);
});
}
// 操作你的数据树
function compose (data) {
var temp = [];
// 递归遍历数据,并提取
function walk (data) {
// 当目标不再是数组的时候停止
if (data instanceof Array) {
data.forEach(function (item) {
temp.push({
id: item.id,
chk: item.chk === true ? '1' : '2'
});
// 递归子数据
walk(item.childen);
});
}
}
walk(data);
return temp;
}
exports.dataParse = dataParse;
exports.compose = compose;
####主进程main.js
// 启动子进程
var child = require('child_process').spawn('node', ['compose.js']);
var data = [..............]; // 填入data数据
// 恢复子进程输出流数据流入
child.stdout.resume();
// 提取子进程输出流数据,并将结果解析为json对象(数组)
// 操作...
require('./util.js').dataParse(child.stdout, function (result) {
var obj = JSON.parse(result);
// any code...
});
// 向子进程写入数据树(字符串 )
// 由子进程接管递归遍历,并将结果返回主进程
child.stdin.write(JSON.stringify(data));
####计算进程compose.js
var util = require('./util.js');
// 恢复输入流数据流入
process.stdin.resume();
// 提取主进程输入流数据,并将结果解析为json对象(数组)
// compose操作数据树,将结果转换为字符串,发送回主进程
util.dataParse(process.stdin, function (result) {
process.stdout.write(JSON.stringify(util.compose(JSON.parse(result))));
});
callback都没有,还callback hell。
递归都不会用,唉
一头雾水
用递归不是很简单的么,修改原程序 for(var i=0; i <= data.length; i++) 中 i<data.length , 小于代替小于等于,同j,k循环过程。
//递归函数
fun=function(data){
for(var i=0; i < data.length; i++) {
if(data[i].children.length>0){
temp.push({
id : data[i].id,
chk: data[i].chk === true ? '1' : '2'
});
fun(data[i].children)
}
}
}
var data = [
{"id": 1,"chk": true,"children": [
{"id": 2,"chk": true,"children": [
{"id": 3,"chk": false,"children": [
{"id": 4,"chk": false,"children": []}
]},{"id": 5,"chk": true,"children": [
{"id": 6,"chk": false,"children": []}
]}
]},{"id": 7,"chk": true,"children": [
{"id": 8,"chk": false,"children": [
{"id": 9,"chk": false,"children": []}
]}
]}
]}
];
var temp = [];
fun(data);
console.log(temp);
//输出
[ { id: 1, chk: ‘1’ },
{ id: 2, chk: ‘1’ },
{ id: 3, chk: ‘2’ },
{ id: 5, chk: ‘1’ },
{ id: 7, chk: ‘1’ },
{ id: 8, chk: ‘2’ } ]
var temp = []; function fun(data){ for(var i = 0, len = data.length; i < len; i++){ temp.push({ id : data[i].id, chk: data[i].chk === true ? ‘1’ : ‘2’ }); if(data[i].children){ fun(data[i].children) } } }
没有callback,就是基本的循环嵌套.
问题描述
你提供的代码中存在深层次的嵌套回调(callback hell),这使得代码难以阅读和维护。我们可以使用async
库或Q
库来优化这段代码,使其更易于理解和管理。
使用 async
库优化代码
async
是一个非常流行的库,可以用来处理复杂的异步操作。我们可以使用 async.each
和 async.eachSeries
来简化嵌套循环。
示例代码:
const async = require('async');
var temp = [];
var data = [/* 你的数据 */];
function processItem(item, callback) {
temp.push({
id: item.id,
chk: item.chk ? '1' : '2'
});
callback();
}
function processChildren(children, callback) {
async.eachSeries(children, (child, cb) => {
temp.push({
id: child.id,
chk: child.chk ? '1' : '2'
});
cb();
}, callback);
}
async.eachSeries(data, (item, callback) => {
processItem(item, () => {
if (item.children && item.children.length > 0) {
processChildren(item.children, () => {
if (item.children[0] && item.children[0].children && item.children[0].children.length > 0) {
async.eachSeries(item.children[0].children, (grandChild, cb) => {
temp.push({
id: grandChild.id,
chk: grandChild.chk ? '1' : '2'
});
cb();
}, callback);
} else {
callback();
}
});
} else {
callback();
}
});
}, (err) => {
if (err) {
console.error(err);
} else {
console.log(temp);
}
});
使用 Q
库优化代码
Q
是另一个处理异步操作的库,可以用来创建和组合Promise对象。这里我们使用 Q.all
来处理嵌套的循环。
示例代码:
const Q = require('q');
var temp = [];
var data = [/* 你的数据 */];
function processItem(item) {
return new Q.Promise((resolve) => {
temp.push({
id: item.id,
chk: item.chk ? '1' : '2'
});
resolve();
});
}
function processChildren(children) {
return Q.all(children.map((child) => {
return new Q.Promise((resolve) => {
temp.push({
id: child.id,
chk: child.chk ? '1' : '2'
});
resolve();
});
}));
}
function main() {
let promises = data.map((item) => {
return processItem(item).then(() => {
if (item.children && item.children.length > 0) {
return processChildren(item.children).then(() => {
if (item.children[0] && item.children[0].children && item.children[0].children.length > 0) {
return Q.all(item.children[0].children.map((grandChild) => {
return new Q.Promise((resolve) => {
temp.push({
id: grandChild.id,
chk: grandChild.chk ? '1' : '2'
});
resolve();
});
}));
}
});
}
});
});
return Q.all(promises).then(() => {
console.log(temp);
}).catch((err) => {
console.error(err);
});
}
main();
解释
async
版本:我们使用了async.eachSeries
来依次处理每一层的数据,从而避免了嵌套回调。Q
版本:我们使用了Q.all
来并行处理多个异步操作,并返回一个 Promise 对象来处理最终的结果。
这两种方法都可以有效地减少嵌套层级,使代码更清晰易读。你可以根据实际需求选择合适的方法。