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'
            });
        }
    }
}


9 回复

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.eachasync.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 对象来处理最终的结果。

这两种方法都可以有效地减少嵌套层级,使代码更清晰易读。你可以根据实际需求选择合适的方法。

回到顶部