我是来自java世界的js和节点的新手。我根据我写的真实程序将测试程序放在一起。它在node.js程序中使用Q库。
for循环仅作用于数组中的最后一项..就像所有其他Q.promises都丢失一样。
我的输出是: registerELB 去registerELB resumeASG 4 开始实例4 resumeASG 4 开始实例4 resumeASG 4 开始实例4 resumeASG 4 开始实例4 autoshutdown完成。
我期望它在哪里:
resumeASG 1 开始实例1 resumeASG 2 开始实例2 resumeASG 3 开始实例3 resumeASG 4 开始实例4 registerELB 去registerELB autoshutdown完成。
循环中发生了什么?承诺是否会丢失?
为什么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;
}
答案 0 :(得分:4)
这是循环中for
循环和任何异步操作的经典问题。因为for
循环中的异步操作在for
循环完成后的某个时间完成,所以在for
循环内设置的任何变量都将在循环结束时具有值。任何异步操作都会完成。
在您的特定情况下,somedata
变量是一个问题。您通常可以通过使用闭包来解决此问题,以便异步操作的每次调用都具有自己的功能,因此它自己的状态在异步操作完成之前一直存在。
在我看来,您可能还会遇到其他同步问题,因为您的for
循环并行运行所有操作,并且您的内部异步操作与您要解析的外部承诺之间没有任何关联,所以我怀疑这里还有其他问题。
但是,如果您只是将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()
循环内)根本没有与外部承诺相关联?那么,getInstances()
将在.forEach()
循环中的内部承诺完成之前解决它的承诺?这对我来说很糟糕。我不知道究竟应该推荐什么,因为我不知道你想并行运行哪些操作,哪些应该是串联的。您可能需要执行Promise.all()
收集来自for
循环的所有承诺,然后使用它来解决您的外部承诺。