在构建数组循环后发送标题后,无法设置标题

时间:2017-05-16 05:41:46

标签: javascript node.js

我试图建立一个对象数组并发送给客户端。收到错误:



    throw new Error('Can\'t set headers after they are sent.');
    ^

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:357:11)
    at ServerResponse.header (D:\Dropbox (Personal)\coding\coinTracker5\node_modules\expr                         ess\lib\response.js:725:10)
    at ServerResponse.send (D:\Dropbox (Personal)\coding\coinTracker5\node_modules\expres                         s\lib\response.js:170:12)
    at ServerResponse.json (D:\Dropbox (Personal)\coding\coinTracker5\node_modules\expres                         s\lib\response.js:256:15)
    at D:\Dropbox (Personal)\coding\coinTracker5\routes\index.js:58:11
    at D:\Dropbox (Personal)\coding\coinTracker5\node_modules\cryptox\lib\index.js:51:9
    at D:\Dropbox (Personal)\coding\coinTracker5\node_modules\cryptox\lib\bitfinex.js:93:                         13
    at D:\Dropbox (Personal)\coding\coinTracker5\node_modules\cryptox\lib\bitfinex.js:72:                         13
    at Request._callback (D:\Dropbox (Personal)\coding\coinTracker5\node_modules\bitfinex                         -api-node\rest.js:95:16)
    at Request.self.callback (D:\Dropbox (Personal)\coding\coinTracker5\node_modules\requ                         est\request.js:187:22)




我认为res.json(rates)只会触发一次,所以我不确定为什么标题会被设置两次?

另外如果有更好的方法来构建这个数组并将其发送给客户端,请赐教!谢谢



var rates = [];
for (var i = 0, len = cryptoxArray.length; i < len; i++) {

  cryptoxArray[i].getRate({
    pair: "XBT_USD"
  }, function(err, rateResponse) {
    if (!err)
      rates.push(rateResponse);

    if (i == len) {
      console.log('rates: ' + rates);
      res.json(rates);
    }
  });
&#13;
&#13;
&#13;

4 个答案:

答案 0 :(得分:1)

您的res.json位于for循环内。循环将完成迭代并在工作之前调用res.json以获取速率。如果您希望获得所有费率,则可能需要等待所有getRate次呼叫完成才能响应。我会使用承诺。

// if getRate returned a promise
var rates = cryptox.map(item => new Promise((resolve, reject) => {
  item.getRate({pair:'XBT_USD'}, (err, rate) => {
    if (err) return reject(err);
    return resolve(rate);
  })
})
Promise.all(rates)
  .then(res.json);

我现在意识到只是说使用Promises并没有多大帮助。这是一个非承诺版本。这是在飞行中放在一起,但应该工作。

function mapAsync (fn, list, cb) {
  var newList = [];
  var i = 0;

  function counter (err, value) {
    console.log('count', value)
    if (err) return cb(err);
    newList.push(value);
    if (i === list.length -1) return cb(newList);
    i++;
    callItem();
  }

  function callItem () {
    return fn(list[i], counter)
  }
  return callItem();
}

function getRateFromItem (item, cb) {
  return i.getRate({pair:'XBT_USD'}, cb);
}

mapAsync(getRateFromItem, cryptoxArray, (err, values) => {
  // as long as there is no err you should have your array of values
})

答案 1 :(得分:1)

当任何回调被调用时,我已经等于长度,你不能使用这样的循环。我的建议是使用promisses并为此或生活做一个Promose.all。

iiefe

for(i, len, etc){
  (function(index){do your stuff here})(i)
}

我个人最喜欢的就是这些。

import Promise from 'bluebird';

Promise.all(
 cryptoxArray.map(
   item => Promise.promisify(item. getRate)({pair: "XBT_USD"})
   .then(rate => rate, error => null)
 ) 
)
.then(rates => rates.filter(rate => rate !== null))
.then(res.json);

答案 2 :(得分:0)

在发送响应之前,只需使用async来等待所有异步回调:

var rates = [];
async.each(cryptoxArray, function(e, callback) {

   e.getRate({
    pair: "XBT_USD"
   }, function(err, rateResponse) {
     if (!err){
       rates.push(rateResponse);
     }
     callback();
   });
},

function(err){
   return res.json(rates);
});

答案 3 :(得分:0)

我最近遇到了一个类似的问题,我正在寻找属于一组属性的所有数据,这些属性属于一个父属性,经过一天的尝试,又找到了以下片段的解决方案:

Model.find({field: req.params.id})
.then(response1 => {
 let myexpected = response1.map(item => new Promise((resolve, reject) => {
   Model2.find({field: item._id}, (err, resolveData) => {
    if (err) return reject(err);
    return resolve(resolveData);
   })
 }));
  Promise.all(myexpected)
    .then(expectedData => {
      console.log(expectedData);
      res.status(200).json({data: expectedData});
    })
})

我希望对您有所帮助,或者对寻求类似解决方案的另一个人有所帮助。