简体   繁体   中英

Looping with Q promises in node.js

I'm very new to js and node coming from a java world. I threw a test program together based on the real program I'm writing. It's using the Q library in a node.js program.

The for loop is only acting on the last item in the array.. it's like all the other Q.promises are lost.

My output is: registerELB de-registerELB resumeASG 4 started instance 4 resumeASG 4 started instance 4 resumeASG 4 started instance 4 resumeASG 4 started instance 4 autoshutdown complete.

Where as I expect it to be:

resumeASG 1 started instance 1 resumeASG 2 started instance 2 resumeASG 3 started instance 3 resumeASG 4 started instance 4 registerELB de-registerELB autoshutdown complete.

  1. what's going on in the loop? Are the promises getting lost?

  2. why is registerELB and de-registerELB happening before my first .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; } 

This is a classic issue with a for loop and any async operation inside the loop. Because your async operations inside the for loop complete some time LATER after the for loop has already finished, any variables set inside your for loop will have the values at the end of the loop when any of your async operations completes.

In your particular case, the somedata variable is a problem here. You can generally work-around this issue by using a closure so that each invocation of your async operations has it's own function and thus it's own state that uniquely survives until the async operation has completed.

It looks to me like you may also have other synchronization issues going on here since your for loop is running all your operations in parallel and you have no link between your inner async operations and the outer promise you are resolving, so I suspect there are other problems here too.

But, if you just change your for loop to a .forEach() loop, then each invocation of the loop will have it's own unique state. I honestly can't say that there aren't other issues in this code (since I have no way of testing it), but this should at least solve one issue related to the for loop:

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

Also, do you realize that your inner promises (inside the .forEach() loop) are not linked at all to the outer promise? So, getInstances() will resolve its promise, long before the inner promises in the .forEach() loop are done? This looks broken to me. I don't know exactly what to recommend because I don't know which operations you want to run in parallel and which should be in series. You may need to do a Promise.all() that collects all the promises from the for loop and then use that to resolve your outer promise.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM