等待Node.js表示应用程序调用http.request的for循环

我的头即将爆炸。 请知道,我等了大约一个星期才发布这个。 单凭耻辱,不能把这个混杂在浪费个人时间的巨大数量上,把我置于一个非常黑暗的地方。 我问你interwebs …请告诉我的方式。

我正在编写我的第一个NodeJS Express 4应用程序,它将pipe理我的家庭自动化系统(具有Rest API)。 杀死我的事情是我有99%的代码完成,我只是不能让它等待循环的http.request完成之前,调用我的callback函数出下面的函数。

我试过,DeAsync,Async,wait.for,…我似乎无法得到它主要是因为在一个for循环。 任何人都可以帮我解决这个问题。 我只是无法把我的脑袋绕过asynchronous或callback,以防止这个问题。

最主要的是它需要一个名为value对象的对象,然后循环它们,调用一个web服务,填充一个我调用的对象:ISYGetData,最后应该触发主callback,然后触发另一个函数来呈现一个web页面模板与响应中的数据。

任何帮助将不胜感激。

function runISYGet(runISYGetInput, resInput, runISYGetCallback) { //console.dir(runISYGetInput); //console.log('length:' + runISYGetInput.length); ISYGetData = []; for (var i = 0; i < runISYGetInput.length; i++) { // runISYGetInput Object Example: // runISYGetInput = [ // { id: "36981", operation: "on" } // , { id: "82563", operation: "on" } // , { id: "52839", operation: "on" } // , { id: "17383", operation: "on" } // , { id: "38863", operation: "on" } console.log('Starting Loop: ISYGet with %s,%s', runISYGetInput[i].id, runISYGetInput[i].operation); if (runISYGetInput[i].operation.toUpperCase() == "ON") { var operationTranslated = "DON" } else { var operationTranslated = "DOF" } if (typeof runISYGetInput[i].intensity === 'undefined') { console.log('Nothing in intensity'); }; var options = { hostname: hostname ,port: port ,path: "/rest/nodes/" + runISYGetInput[i].id + "/cmd/" + operationTranslated + "/" ,method: 'GET' ,auth: username + ':' + password } var req = https.request(options, function (res) { console.log("Inside httpGet | Calling ISY Now"); //console.log("statusCode: ", res.statusCode); //console.log("headers: ", res.headers); res.on('data', function (chunk) { console.log('BODY: ' + chunk); if (i == 0) { ISYGetData = [{ hostname: options.hostname ,port: options.port ,path: options.path ,statusCode: res.statusCode ,responseHeaders: JSON.stringify(res.headers) ,body: chunk.toString('utf8') }] } else { ISYGetData = ISYGetData.concat([{ hostname: options.hostname ,port: options.port ,path: options.path ,statusCode: res.statusCode ,responseHeaders: JSON.stringify(res.headers) ,body: chunk.toString('utf8') }]) }; }); req.on('error', function (e) { console.error(e); }); }); req.end(); } console.log('This should happen last but doesnt currently, want to run runISYGetCallback(ISYGetData, resInput) after http gets are done and ISYGetData is populated'); // This callback renders the page with all needed data from the ISYGetData object runISYGetCallback(ISYGetData, resInput); 

}

下面是一个为每个https.request()操作创build一个承诺的方法,然后使用Promise.all()等待所有这些承诺完成,并允许承诺基础设施自动将所有数据收集到一个数组中与要求的顺序相同。

 function runISYGet(runISYGetInput, resInput, runISYGetCallback) { var operationTranslated, promises = [], options; for (var i = 0; i < runISYGetInput.length; i++) { if (runISYGetInput[i].operation.toUpperCase() == "ON") { operationTranslated = "DON"; } else { operationTranslated = "DOF"; } if (typeof runISYGetInput[i].intensity === 'undefined') { console.log('Nothing in intensity'); } // create new options object for each cycle through the loop options = { hostname: hostname, port: port, path: "/rest/nodes/" + runISYGetInput[i].id + "/cmd/" + operationTranslated + "/", method: 'GET', auth: username + ':' + password }; promises.push(new Promise(function(resolve, reject) { // save options locally because it will be reassigned to a different object // before it gets used in the callback below var localOptions = options; var req = https.request(localOptions, function (res) { var data = ""; res.on('data', function (chunk) { data += chunk; }); res.on('end', function() { // resolve with the accumulated data // do it this way so that the promise infrastructure will order it for us resolve({ hostname: localOptions.hostname, port: localOptions.port, path: localOptions.path, statusCode: res.statusCode, responseHeaders: JSON.stringify(res.headers), body: data.toString('utf8') }); }); }); req.on('error', function (e) { console.error(e); reject(e); }); req.end(); })); } // now wait for all promises to be done Promise.all(promises).then(function(allData) { // This callback renders the page with all needed data // when all the https.request() calls are done runISYGetCallback(allData, resInput); }, function(err) { // figure out what to do here when there was an error in one or more of the https.request() calls }); } 

注意:如果使用为请求模块提供请求的请求 – 承诺模块,那么您可以更加简化,但是我select了显示不依赖任何新模块的实现

部分事物清单已更改:

  1. 为循环中的每个请求创build一个新的承诺
  2. 等待所有的承诺,使用Promise.all()
  3. 捕捉选项对象,每次通过循环,所以我们使用的选项对象不会被asynchronous处理期间的下一个循环通过循环覆盖
  4. 让承诺捕获所有的结果,以便他们按照正确的顺序(而不是在他们到达时连接它们)
  5. 将所有到达每个请求的数据块集中到一块(因为数据可以以多个块的forms到达)
  6. 根据您未使用的res.on('end')触发完成承诺
  7. req.on('error')移到它所属的更高级别
  8. 根据请求错误使用承诺拒绝将错误传播回最高级别
  9. for循环之外移动局部variables的声明。

仅供参考,您可能希望用一个承诺来replacerunISYGetCallback,只需从runISYGet函数返回承诺,如下所示:

 function runISYGet(runISYGetInput, runISYGetCallback) { var operationTranslated, promises = [], options; for (var i = 0; i < runISYGetInput.length; i++) { if (runISYGetInput[i].operation.toUpperCase() == "ON") { operationTranslated = "DON"; } else { operationTranslated = "DOF"; } if (typeof runISYGetInput[i].intensity === 'undefined') { console.log('Nothing in intensity'); } // create new options object for each cycle through the loop options = { hostname: hostname, port: port, path: "/rest/nodes/" + runISYGetInput[i].id + "/cmd/" + operationTranslated + "/", method: 'GET', auth: username + ':' + password }; promises.push(new Promise(function(resolve, reject) { // save options locally because it will be reassigned to a different object // before it gets used in the callback below var localOptions = options; var req = https.request(localOptions, function (res) { var data = ""; res.on('data', function (chunk) { data += chunk; }); res.on('end', function() { // resolve with the accumulated data // do it this way so that the promise infrastructure will order it for us resolve({ hostname: localOptions.hostname, port: localOptions.port, path: localOptions.path, statusCode: res.statusCode, responseHeaders: JSON.stringify(res.headers), body: data.toString('utf8') }); }); }); req.on('error', function (e) { console.error(e); reject(e); }); req.end(); })); } // return master promise that is resolved when all the other promises are done return Promise.all(promises); } // usage runISYGet(...).then(function(allData) { // process the data here }, function(err) { // handle error here });