NodeJS将Promises转换为Promise.all

时间:2016-04-04 01:21:11

标签: javascript node.js asynchronous promise

我有一个我一直在研究的NodeJS脚本,但我遇到的最大问题是链接所有这些承诺不仅难看,而且随着时间的推移难以维护。

我想使用Promise.all()方法将这些单独的promises转换为一个,但我不确定如何使用此方法获得相同的功能并将变量从一个promise分配给另一个promise。

例如,我的第二个承诺:methods.login()返回几乎所有其他承诺中使用的sessionId。我如何使用Promise.all() ??

分配该变量并将其传递给进一步的相关承诺

这是我目前的代码:

var zabbixApi = require('./zabbixapi.js');
var methods   = require('./methods.js');
var fs        = require('fs');
var SESSIONID;

function main() {
    var apiVersion, loggedOut, numTemplates, numHosts, numHostGroups, numItems;
    var templates = []
        , hostGroups = []
        , hosts = [];

    /*var promises = [];
    promises.push(zabbixApi(methods.getApiVersion()));
    promises.push(zabbixApi(methods.login(SESSIONID)));
    promises.push(zabbixApi(methods.getHostGroups(SESSIONID)));
    promises.push(zabbixApi(methods.getTemplates(SESSIONID)));
    promises.push(zabbixApi(methods.getHosts(SESSIONID)));
   // promises.push(zabbixApi(methods.configExport(hostIds, templateIds, groupIds, SESSIONID)));
    promises.push(zabbixApi(methods.logout(SESSIONID)));

    Promise.all(promises).then(function (values) {
       console.log('All promises completed.');

    }, function (reason) {
        console.log('Error completing promises: ' + reason);
    });*/


    // Get API version
    zabbixApi(methods.getApiVersion())

    // If successful, login to the API
    .then(function (version) {
        apiVersion = version.result;

        // Verify that the API version returned is correct
        if (apiVersion.length < 5 || !apiVersion) {
            console.log('Error occurred retrieving API version: ' + version.error.data);
            return 1;
        } else {
            return zabbixApi(methods.login(SESSIONID));
        }

    }, function (error) {
        console.log('Error occurred retrieving API version: ' + error);
        return 1;

        // If login successful, continue operations until logged out or error
    }).then(function (auth) {
        SESSIONID = auth.result;

        if (!SESSIONID) {
            console.log('Error retrieving session id: ' + auth.error.data);
            return 1;
        } else {
            console.log('Logged in successfully!');
            return zabbixApi(methods.getHostGroups(SESSIONID));
        }

    }, function (error) {
        console.log('Error occurred authenticating: ' + error);
        return 1;

        // Attempt to retrieve all hostgroup information
    }).then(function (hostgroups) {
        numHostGroups = hostgroups.result.length;
        hostGroups = hostgroups.result;

        if (!numHostGroups) {
            console.log('Error occurred retrieving host groups: ' + hostgroups.error.data);
            return 1;
        } else {
            return zabbixApi(methods.getTemplates(SESSIONID));
        }


    }, function (error) {
        console.log('Error occurred retrieving host groups: ' + error);
        return 1;

        // Attempt to retrieve host information
    }).then(function (template) {
        numTemplates = template.result.length;
        templates = template.result;

        if (!numTemplates) {
            console.log('Error occurred retrieving templates: ' + template.error.data);
            return 1;
        } else {
            return zabbixApi(methods.getHosts(SESSIONID));
        }


    }, function (error) {
        console.log('Error occurred retrieving templates: ' + error);
        return 1;

        // Attempt to retrieve host information
    }).then(function (hosts) {
        numHosts = hosts.result.length;
        hosts    = hosts.result;

        if (!numHosts) {
            console.log('Error occurred retrieving host groups: ' + hosts.error.data);
            return 1;
        } else {
            var groupIds      = []
                , hostIds     = []
                , templateIds = [];

            // Extract all groupIds for host groups
            for (var i = 0; i < numHostGroups; i++) {
                groupIds[i] = hostGroups[i].groupid;
            }
            // Extract all hostIds for hosts
            for (var i = 0; i < numHosts; i++) {
                hostIds[i] = hosts[i].hostid;
            }
            // Extract all templateIds for templates
            for (var i = 0; i < numTemplates; i++) {
                templateIds[i] = templates[i].templateid;
            }
            return zabbixApi(methods.configExport(hostIds, templateIds, groupIds, SESSIONID));
        }

    }, function (error) {
        console.log('Error occurred retrieving host groups: ' + error);
        return 1;

        // Attempt to retrieve configuration information
    }).then(function (config) {
        //console.log(config);
        if (config.error) {
            console.log('Error occurred retrieving configuration: ' + config.error.message + ': ' + config.error.data);
            return 1;
        } else {
            if (!writeToFile(config)) {
                return 1;
            } else {
                console.log('Configuration details exported successfully.');

            }
            return zabbixApi(methods.logout(SESSIONID));
        }

    }, function (error) {
        console.log('Error occurred retrieving configuration: ' + error);
        return 1;

        // Attempt to logout of API, if logout successful exit safely
    }).then(function (logout) {
        loggedOut = logout.result;
        if (!loggedOut) {
            console.log('Error logging out: ' + logout.error.data);
            return 1;
        } else {
            console.log('Logged out successfully!');
            return 0;
        }

    }, function (error) {
        console.log('Error occurred logging out: ' + error);
        return 1;
    });
}

function writeToFile (config) {
    fs.writeFile('zabbix_config_export.json', JSON.stringify(config), function (err) {
        if (err) {
            return console.log('Error writing configuration to file: ' + err);
        }
    });
    return true;
}
main();

2 个答案:

答案 0 :(得分:2)

您可以对可以并行运行的操作使用Promise.all(),但不能对必须按特定顺序执行的操作使用getApiVersion Login to session In parallel (getHostGroups, getTemplates, getHosts) configExport previous results In parallel (logout, writeToFile)

在查看代码时,您似乎确实有几个地方可以并行执行某些操作,但并非所有操作都可以通过这种方式完成。看来你可以按照这个一般顺序做事:

var zabbixApi = require('./zabbixapi.js');
var methods   = require('./methods.js');
var fs        = require('fs');
var SESSIONID;

function logout() {
    if (SESSIONID) {
        var p = zabbixApi(methods.logout(SESSIONID));
        // clear SESSIONID to show that we've already launched a logout attempt, no need to try again
        SESSIONID = null;
        return p;
    } else {
        return Promise.resolve();
    }
}

function main() {
    var apiVersion, hostGroups, templates, hosts;

    // Get API version
    zabbixApi(methods.getApiVersion())

    // If successful, login to the API
    .then(function (version) {
        apiVersion = version.result;

        // Verify that the API version returned is correct
        if (!apiVersion || apiVersion.length < 5) {
            throw new Error('Error occurred retrieving API version: ' + version.error.data);
        } else {
            return zabbixApi(methods.login(SESSIONID));
        }

    }, function (error) {
        throw new Error('Error occurred retrieving API version: ' + error);

    // If login successful, continue operations until logged out or error
    }).then(function (auth) {
        SESSIONID = auth.result;

        if (!SESSIONID) {
            throw new Error('Error retrieving session id: ' + auth.error.data);
        } else {
            console.log('Logged in successfully!');

            // now that we are logged in, a number of operations can be launched in parallel
            return Promise.all([
                zabbixApi(methods.getHostGroups(SESSIONID),
                zabbixApi(methods.getTemplates(SESSIONID),
                zabbixApi(methods.getHosts(SESSIONID)
            ]);
        }
    }, function (error) {
        throw new Error('Error occurred authenticating: ' + error);

    // we have hostGroups, templates and hosts here
    }).then(function(r) {
        // r[0] = hostGroups, r[1] = templates, r[2] = hosts

        // check hostGroups
        hostGroups = r[0].result;
        if (!hostGroups.length) {
            throw new Error('Error occurred retrieving host groups: ' + hostgroups.error.data);
        }

        // check templates
        templates = r[1].result;
        if (!templates.length) {
            throw new Error('Error occurred retrieving templates: ' + template.error.data);
        }

        // check host information
        hosts = r[2].result;
        if (!hosts.length) {
            throw new Error('Error occurred retrieving host groups: ' + hosts.error.data);
        }

        // utility function for retrieving a specific property from each array of objects
        function getIds(array, prop) {
            return array.map(function(item) {
                return item[prop];
            });
        }

        var groupIds = getIds(hostGroups, "groupid");
        var hostIds = getIds(hosts, "hostid");
        var templateIds = getIds(templates, "templateid");
        return zabbixApi(methods.configExport(hostIds, templateIds, groupIds, SESSIONID));
    }).then(function(config) {
        if (config.error) {
            throw new Error('Error occurred retrieving configuration: ' + config.error.message + ': ' + config.error.data);
        }
        // simultaneously write to file and logout (since these are not dependent upon one another)
        return Promise.all(logout(), writeToFile(config));
    }).then(function() {
        // success here, everything done
    }, function(err) {
        // upon error, try to logout and rethrow earlier error
        return logout().then(function() {
            throw err;
        }, function() {
            throw err;
        });
    }).then(null, function(err) {
        // error here
        console.log(err);
    });

}

function writeToFile (config) {
    return new Promise(function(resolve, reject) {
        fs.writeFile('zabbix_config_export.json', JSON.stringify(config), function (err) {
            if (err) {
                return Promise.reject(new Error('Error writing configuration to file: ' + err));
            }
            resolve();
        });
    });
}
main();

这可以这样实现:

Promise.all()

这也使得一些其他重要的结构变化/修复:

  1. 任何时候你有拒绝/捕获处理程序,如果你没有返回被拒绝的承诺或从该处理程序抛出,那么承诺链将继续,这不是你想要的,你几乎每次拒绝都这样做你有的处理程序。
  2. 不需要为每一个承诺都有拒绝处理程序。如果您只是打算拒绝任何拒绝,那么您可以确保拒绝原因是描述性的,并且在链的末尾记录一个地方的所有失败。
  3. 这在两个地方使用writeToFile(),其中操作可以并行运行,因为它们不相互依赖,但代码逻辑想要知道它们何时完成。
  4. logout()已更改为返回承诺。
  5. 创建一个可以在多个错误路径中安全调用的apiVersion函数,以便所有错误和成功路径至少尝试注销(如果登录成功)
  6. 检查!apiVersion时交换这两个条件,因为您应先检查Visual Studio 2015

答案 1 :(得分:1)

Promises.all用于在并行运行时处理异步函数,您需要等到它们全部完成后才能运行。您共享的代码中的承诺不是独立的,因此您无法在此处使用Promises.all。查看this reference以查看Promises.all用法。

相关问题