Node.js / Javascript 多重递归承诺在视图中返回

Posted

技术标签:

【中文标题】Node.js / Javascript 多重递归承诺在视图中返回【英文标题】:Node.js / Javascript multiple recursive promise to return at view 【发布时间】:2019-12-19 08:32:13 【问题描述】:

我目前有一个包含多个递归调用的算法,我希望每个人都返回该算法以巩固我的结果

问题是递归调用太多,我不再知道如何返回我的合并结果

我尝试在每个promise的末尾通过结果数检查promise的数量来做出promise.all,但是我得到的结果表明我正在做出几个http响应

在这个版本中,我在所有承诺执行或添加到我的承诺列表之前返回结果。

var https = require('https');
var moment = require('moment');

app.get('/detail/:issue', function (req, res) 

    var promises = [];
    var jsonResult = 
        total: 
            daysSpent: 0,
            daysEstimated: 0,
            daysRemaining: 0,
            cost: 0
        ,
        issues: 
    ;

    var getIssue = function (key) 

        /**
         * Récupération des imputations par projet.
         */
        promises.push(new Promise(function (resolve) 

            var options = Object.assign(, app.locals.data.jira);
            options.path = "/jira/rest/api/2/issue/issueKey"
                .replace('issueKey', key);

            https.request(options, (resp) => 
                let body = '';
                resp.on('data', (chunk) => 
                    body += chunk;
                );
                resp.on('end', () => 

                    var issue = JSON.parse(body);

                    if (issue.fields.issuetype.name == 'Epic') 
                        getEpic(issue.key);
                     else 

                        jsonResult.issues[issue.key] = 
                            key: issue.key,
                            numberTicket: issue.fields.customfield_10267 === null ? "-" : issue.fields.customfield_10267,
                            icon: issue.fields.issuetype.iconUrl,
                            name: issue.fields.summary,
                            status: issue.fields.status.name,
                            daysEstimated: issue.fields.issuetype.subtask ? (((issue.fields.timeoriginalestimate || 0) / 3600) / 7) : ((issue.fields.customfield_11901 || 0) / 7),
                            daysRemaining: issue.fields.issuetype.subtask ? (((issue.fields.timeoriginalestimate || 0) / 3600) / 7) : ((issue.fields.customfield_11901 || 0) / 7),
                            hoursSpent: 0,
                            daysSpent: 0,
                            cost: 0,
                            parent: issue.fields.parent === undefined ? issue.key : issue.fields.parent.key,
                            detail: ,
                            subtask: issue.fields.issuetype.subtask,
                            worklog: issue.fields.worklog.total != 0
                        

                        jsonResult.total.daysEstimated += ((issue.fields.customfield_11901 || 0) / 7);
                        jsonResult.total.daysRemaining += ((issue.fields.customfield_11901 || 0) / 7);

                        if (issue.fields.subtasks != false) 
                            for (let e in issue.fields.subtasks) 
                                e = issue.fields.subtasks[e];
                                getIssue(e.key);
                            ;
                        

                        if (issue.fields.worklog.total != 0) 
                            getWorklog(issue.key);
                        
                    

                    resolve();

                );
            ).on("error", (e) => 
                console.log("Error: " + e.message);
            ).end();
        ));

    

    var getEpic = function (key) 

        /**
         * Récupération des imputations par projet.
         */
        promises.push(new Promise(function (resolve) 

            var postData = JSON.stringify(
                "jql": "'Epic link' = issueKey"
                    .replace('issueKey', key),
                "maxResults": -1,
                "fields": [
                    "issuekey"
                ]
            );

            var options = Object.assign(, app.locals.data.jira);
            options.path = "/jira/rest/api/2/search";
            options.method = 'POST';
            options.headers = 
                'Content-Type': 'application/json',
                'Cache-Control': 'no-cache',
                "Content-Length": Buffer.byteLength(postData)
            ;

            var req = https.request(options, (resp) => 
                let body = '';
                resp.on('data', (chunk) => 
                    body += chunk;
                );
                resp.on('end', () => 

                    var issues = JSON.parse(body).issues;

                    for (let issue in JSON.parse(body).issues) 
                        issue = issues[issue];
                        getIssue(issue.key);
                    ;

                    resolve();

                );
            ).on("error", (e) => 
                console.log("Error: " + e.message);
            );

            req.write(postData);
            req.end();
        ));

    

    var getWorklog = function (key) 

        /**
         * Récupération des imputations par projet.
         */
        promises.push(new Promise(function (resolve) 

            var options = Object.assign(, app.locals.data.jira);
            options.path = "/jira/rest/api/2/issue/issueKey/worklog"
                .replace('issueKey', key);

            https.request(options, (resp) => 
                let body = '';
                resp.on('data', (chunk) => 
                    body += chunk;
                );
                resp.on('end', () => 

                    var worklogs = JSON.parse(body).worklogs;

                    for (let e in worklogs) 

                        e = worklogs[e];

                        if (jsonResult.issues[key].detail[e.author.key] == undefined) 
                            jsonResult.issues[key].detail[e.author.key] = 
                                name: e.author.displayName,
                                hoursSpent: 0,
                                daysSpent: 0,
                                cost: 0
                            
                        

                        jsonResult.issues[key].hoursSpent += e.timeSpentSeconds / 3600;
                        jsonResult.issues[key].detail[e.author.key].hoursSpent += e.timeSpentSeconds / 3600;

                        if (app.locals.data.scr[moment(e.started).format("Y")] !== undefined && app.locals.data.scr[moment(e.started).format("Y")][e.author.emailAddress] !== undefined) 

                            var time = (e.timeSpentSeconds / 3600) / app.locals.data.scr[moment(e.started).format("Y")][e.author.emailAddress].modality;
                            var cost = time * app.locals.data.scr[moment(e.started).format("Y")][e.author.emailAddress].scr;

                            jsonResult.issues[key].detail[e.author.key].daysSpent += time;
                            jsonResult.issues[key].detail[e.author.key].cost += cost;

                            jsonResult.issues[key].daysSpent += time;
                            jsonResult.issues[key].cost += cost;
                            jsonResult.issues[key].daysRemaining -= time;

                    ;

                    resolve();

                );
            ).on("error", (e) => 
                console.log("Error: " + e.message);
            ).end();

        ));

    

    getIssue(req.params.issue);

    Promise.all(promises).then(function () 
        res.json(jsonResult);
    );

);

【问题讨论】:

递归对于某些问题可能是一种很好的技术;在这种情况下,它增加了太多的认知开销(这意味着它很难跟踪程序执行)。您是否尝试过迭代方法?什么条件终止递归?将所有这些函数拆分为单独的文件也很有用。 我建议你使用async/await (developer.mozilla.org/en-US/docs/Web/javascript/Reference/…) 而不是promise。这将允许您编写函数调用,就好像它们是同步函数一样 不可能进行迭代,因为我事先不知道必须处理的元素数量。我处理多达 3 个层次级别的数据,其中第 2 级可以有 X 个子级。我将再次尝试等待/异步解决方案,这是我认为我可以做到的一种方法,但 nodeJS 告诉我我不明白 【参考方案1】:

我的问题已经解决了!

var https = require('https');
var moment = require('moment');

app.get('/detail/:issue', function (req, res) 

    var jsonResult = 
        total: 
            daysSpent: 0,
            daysEstimated: 0,
            daysRemaining: 0,
            cost: 0
        ,
        issues: 
    ;

    var getIssue = function (key) 

        /**
         * Récupération des imputations par projet.
         */
        return new Promise(function (resolve) 

            var options = Object.assign(, app.locals.data.jira);
            options.path = "/jira/rest/api/2/issue/issueKey"
                .replace('issueKey', key);

            https.request(options, (resp) => 
                let body = '';
                resp.on('data', (chunk) => 
                    body += chunk;
                );
                resp.on('end', () => 

                    var promises = [];
                    var issue = JSON.parse(body);

                    if (issue.fields.issuetype.name == 'Epic') 
                        promises.push(getEpic(issue.key));
                     else 

                        jsonResult.issues[issue.key] = 
                            key: issue.key,
                            numberTicket: issue.fields.customfield_10267 === null ? "-" : issue.fields.customfield_10267,
                            icon: issue.fields.issuetype.iconUrl,
                            name: issue.fields.summary,
                            status: issue.fields.status.name,
                            daysEstimated: issue.fields.issuetype.subtask ? (((issue.fields.timeoriginalestimate || 0) / 3600) / 7) : ((issue.fields.customfield_11901 || 0) / 7),
                            daysRemaining: issue.fields.issuetype.subtask ? (((issue.fields.timeoriginalestimate || 0) / 3600) / 7) : ((issue.fields.customfield_11901 || 0) / 7),
                            hoursSpent: 0,
                            daysSpent: 0,
                            cost: 0,
                            parent: issue.fields.parent === undefined ? issue.key : issue.fields.parent.key,
                            detail: ,
                            subtask: issue.fields.issuetype.subtask,
                            worklog: issue.fields.worklog.total != 0
                        

                        jsonResult.total.daysEstimated += ((issue.fields.customfield_11901 || 0) / 7);
                        jsonResult.total.daysRemaining += ((issue.fields.customfield_11901 || 0) / 7);

                        if (issue.fields.worklog.total != 0) 
                            promises.push(getWorklog(issue.key));
                        

                        if (issue.fields.subtasks != false) 
                            for (let e in issue.fields.subtasks) 
                                e = issue.fields.subtasks[e];
                                promises.push(getIssue(e.key));
                            ;
                        
                    

                    Promise.all(promises).then(function () 
                        resolve();
                    );

                );
            ).on("error", (e) => 
                console.log("Error: " + e.message);
            ).end();

        );
    

    var getEpic = function (key) 

        /**
         * Récupération des imputations par projet.
         */
        return new Promise(function (resolve) 

            var postData = JSON.stringify(
                "jql": "'Epic link' = issueKey"
                    .replace('issueKey', key),
                "maxResults": -1,
                "fields": [
                    "issuekey"
                ]
            );

            var options = Object.assign(, app.locals.data.jira);
            options.path = "/jira/rest/api/2/search";
            options.method = 'POST';
            options.headers = 
                'Content-Type': 'application/json',
                'Cache-Control': 'no-cache',
                "Content-Length": Buffer.byteLength(postData)
            ;

            var req = https.request(options, (resp) => 
                let body = '';
                resp.on('data', (chunk) => 
                    body += chunk;
                );
                resp.on('end', () => 

                    var promises = [];
                    var issues = JSON.parse(body).issues;

                    for (let issue in JSON.parse(body).issues) 
                        issue = issues[issue];
                        promises.push(getIssue(issue.key));
                    ;

                    Promise.all(promises).then(function () 
                        resolve();
                    );

                );
            ).on("error", (e) => 
                console.log("Error: " + e.message);
            );

            req.write(postData);
            req.end();

        );
    

    var getWorklog = function (key) 

        /**
         * Récupération des imputations par projet.
         */
        return new Promise(function (resolve) 

            var options = Object.assign(, app.locals.data.jira);
            options.path = "/jira/rest/api/2/issue/issueKey/worklog"
                .replace('issueKey', key);

            https.request(options, (resp) => 
                let body = '';
                resp.on('data', (chunk) => 
                    body += chunk;
                );
                resp.on('end', () => 

                    var worklogs = JSON.parse(body).worklogs;

                    for (let e in worklogs) 

                        e = worklogs[e];

                        if (jsonResult.issues[key].detail[e.author.key] == undefined) 
                            jsonResult.issues[key].detail[e.author.key] = 
                                name: e.author.displayName,
                                hoursSpent: 0,
                                daysSpent: 0,
                                cost: 0
                            
                        

                        jsonResult.issues[key].hoursSpent += e.timeSpentSeconds / 3600;
                        jsonResult.issues[key].detail[e.author.key].hoursSpent += e.timeSpentSeconds / 3600;

                        if (app.locals.data.scr[moment(e.started).format("Y")] !== undefined && app.locals.data.scr[moment(e.started).format("Y")][e.author.emailAddress] !== undefined) 

                            var time = (e.timeSpentSeconds / 3600) / app.locals.data.scr[moment(e.started).format("Y")][e.author.emailAddress].modality;
                            var cost = time * app.locals.data.scr[moment(e.started).format("Y")][e.author.emailAddress].scr;

                            jsonResult.issues[key].detail[e.author.key].daysSpent += time;
                            jsonResult.issues[key].detail[e.author.key].cost += cost;

                            jsonResult.issues[key].daysSpent += time;
                            jsonResult.issues[key].cost += cost;
                            jsonResult.issues[key].daysRemaining -= time;

                            if (jsonResult.issues[key].subtask) 
                                jsonResult.issues[jsonResult.issues[key].parent].hoursSpent += e.timeSpentSeconds / 3600;
                                jsonResult.issues[jsonResult.issues[key].parent].daysSpent += time;
                                jsonResult.issues[jsonResult.issues[key].parent].cost += cost;
                                jsonResult.issues[jsonResult.issues[key].parent].daysRemaining -= time;
                            

                            jsonResult.total.daysSpent += time;
                            jsonResult.total.cost += cost;
                            jsonResult.total.daysRemaining -= time;
                        
                    ;

                    resolve();

                );
            ).on("error", (e) => 
                console.log("Error: " + e.message);
            ).end();

        );

    

    getIssue(req.params.issue).then(function () 
        res.json(jsonResult);
    );

);

每个方法都等待其所有子方法结束才能结束

【讨论】:

以上是关于Node.js / Javascript 多重递归承诺在视图中返回的主要内容,如果未能解决你的问题,请参考以下文章

Node.js资讯 | ES6 下的函数式:递归模式;JavaScript 深拷贝性能分析

无法通过递归调用node.js中的函数从promises获得响应

在 node.js 中承诺一个递归函数

Node.js fs.readdir 递归目录搜索

Node.js fs.readdir 递归目录搜索

Node.js fs.readdir 递归目录搜索