在 node.js 中循环使用 Q promises

Looping with Q promises in node.js

我对来自 java 世界的 js 和节点非常陌生。我根据我正在编写的真实程序编写了一个测试程序。它在 node.js 程序中使用 Q 库。

for 循环仅作用于数组中的最后一项。就像所有其他 Q.promises 都丢失了一样。

我的输出是: 注册ELB 注销ELB 简历 ASG 4 启动实例 4 简历 ASG 4 启动实例 4 简历 ASG 4 启动实例 4 简历 ASG 4 启动实例 4 自动关机完成。

如我所料:

恢复 ASG 1 启动实例 1 恢复 ASG 2 启动实例 2 恢复 ASG 3 启动实例 3 简历 ASG 4 启动实例 4 注册ELB 注销ELB 自动关机完成。

  1. 循环中发生了什么?承诺会丢失吗?

  2. 为什么 registerELB 和 de-registerELB 发生在我的第一个 .then()

    之前

    var Q = require('q'); var startedInstances = [];

    test();
    
    function test() {
        getInstances()
        .then(function(data) {
            var promise = Q.defer();
    
            for(var x = 0; x < data.length; x++) {
                var somedata = data[x];
                console.log(somedata);
    
                startOrStop(somedata)
                .then(function (updateStatus) {
                    var promiseinner = Q.defer();
                    console.log(somedata);
                    if(updateStatus == 'start') {
                        return Q.nfcall(resumeASG, somedata)
                        .then(Q.nfcall(startInstance, somedata));
                    } else if(updateStatus == 'stop') { 
                        return Q.nfcall(suspendASG, somedata)
                        .then(Q.nfcall(stopInstance, somedata));
                    }
                    promiseinner.resolve('complete');
    
                    return promiseinner.promise;
                })
            }
            promise.resolve('successful');
    
            return promise.promise;
    
        }, function(error) {
            console.log('Failed to get instance data ' + error);
            //context.fail();
        }).then(function(data) {
            return registerELB();
        }, function(error) {
            console.log('Failed to Re-register ELB ' + error);
            //context.fail();
        }).then(function(data) {
            console.log('autoshutdown complete.')
            //context.succeed(data);
        }, function(error) {
            console.log('autoshutdown failed to complete. ' + error)
            //context.fail();
        }).done();
    };
    
    function getInstances() {
        var promise = Q.defer();
    
        startedInstances.push(1);
        startedInstances.push(2);
        startedInstances.push(3);
        startedInstances.push(4);
    
        promise.resolve(startedInstances);
        return promise.promise;
    }
    
    function suspendASG (asg) {
        var promise = Q.defer();
    
        console.log('suspendASG ' + asg);
        promise.resolve(asg);
    
        return promise.promise;
    }
    
    function resumeASG (asg) {
        var promise = Q.defer();
    
        console.log('resumeASG ' + asg);
        promise.resolve(asg);
    
        return promise.promise;
    }
    
    function registerELB() {
        var promise  = Q.defer();
    
        console.log('registerELB ');
        console.log('de-registerELB ');
        promise.resolve('success elb');
    
        return promise.promise;
    }
    
    function startInstance(instanceId) {
        var promise = Q.defer();
    
        console.log('started instance ' + instanceId);
        promise.resolve(instanceId);
    
        return promise.promise;
    }
    
    function stopInstance(instanceId) {
    var promise = Q.defer();
    
        console.log('stopped instance ' + instanceId);
        promise.resolve(instanceId);
    
        return promise.promise;
    }
    
    function startOrStop (instance) {
        var promise = Q.defer();
    
    
        promise.resolve('start');
    
        return promise.promise;
    }
    

这是 for 循环和循环内任何异步操作的经典问题。因为 for 循环内的异步操作在 for 循环完成后的某个时间完成,所以 for 循环内设置的任何变量都将在循环结束时具有值您的任何异步操作都已完成。

在您的特定情况下,somedata 变量在这里是一个问题。您通常可以通过使用闭包来解决这个问题,这样每次异步操作调用都有它自己的函数,因此它自己的状态在异步操作完成之前唯一存在。

在我看来,您可能还会遇到其他同步问题,因为您的 for 循环是 运行 并行执行所有操作,而您之间没有 link你内部的异步操作和你正在解决的外部承诺,所以我怀疑这里也有其他问题。

但是,如果您只是将 for 循环更改为 .forEach() 循环,那么循环的每次调用都会有其自己独特的状态。老实说,我不能说此代码中没有其他问题(因为我无法对其进行测试),但这至少应该解决一个与 for 循环相关的问题:

test();

function test() {
    getInstances()
    .then(function(data) {
        var promise = Q.defer();

        // -------- Change made here -------------
        // change for loop to .forEach() loop here
        // to create closure so your async operations
        // each can access their own data
        data.forEach(function(somedata) {
            console.log(somedata);

            startOrStop(somedata)
            .then(function (updateStatus) {
                var promiseinner = Q.defer();
                console.log(somedata);
                if(updateStatus == 'start') {
                    return Q.nfcall(resumeASG, somedata)
                    .then(Q.nfcall(startInstance, somedata));
                } else if(updateStatus == 'stop') { 
                    return Q.nfcall(suspendASG, somedata)
                    .then(Q.nfcall(stopInstance, somedata));
                }
                promiseinner.resolve('complete');

                return promiseinner.promise;
            })
        });
        promise.resolve('successful');

        return promise.promise;

    }, function(error) {
        console.log('Failed to get instance data ' + error);
        //context.fail();
    }).then(function(data) {
        return registerELB();
    }, function(error) {
        console.log('Failed to Re-register ELB ' + error);
        //context.fail();
    }).then(function(data) {
        console.log('autoshutdown complete.')
        //context.succeed(data);
    }, function(error) {
        console.log('autoshutdown failed to complete. ' + error)
        //context.fail();
    }).done();
};

此外,您是否意识到您的内部承诺(在 .forEach() 循环内)根本没有 link 与外部承诺相关?那么, getInstances() 会在 .forEach() 循环中的内部承诺完成之前很久就解决它的承诺?这对我来说看起来很糟糕。我不知道具体推荐什么,因为我不知道哪些操作要 运行 并行,哪些应该串行。你可能需要做一个 Promise.all()for 循环中收集所有的承诺,然后用它来解决你的外部承诺。