Search code examples
meteorcoffeescriptiron-router

Iron Router Server Side Routing callback doesn't work


I am newer for IronRouter, why readFile callback executed the response are send to client.

Router.map(
  ()->
    this.route 'readFile',
      path: '/readFile'
      where: 'server'
      method: 'GET'
      action: ()->
        self = this
        fs.readFile '/tmp/a.txt', (err, data)->
          if err
            throw err
          console.log(data.toString())
          self.response.writeHead(200, {'Content-Type': 'text/plain'})
          self.response.end(data)
        console.log('response ...')

)

http.js:733
  W2049-12:04:26.781(8)? (STDERR)     throw new Error('Can\'t render headers after they are sent to the client.'
  W2049-12:04:26.781(8)? (STDERR)           ^
  W2049-12:04:26.782(8)? (STDERR) Error: Can't render headers after they are sent to the client.

But, I use express , like this is work well.

exports.index = function(req, res){
  fs.readFile('/tmp/a.txt', function (err, data) {
    if (err) throw err;
    console.log(data.toString());
    res.send(200, data.toString());
  });
  console.log('response ...');
};

thanks @ChristianF @Tarang Use Meteor._wrapAsync or Future all work well . when I use self define function replace fs.readFile. It take throw ex . I Doubt My defined function has error. As follows:

@getAccounts = (callback) ->
 query = "SELECT Id, Name, AccountNumber FROM Account"
 queryBySoql(query, (err, result)->
   if err
     return console.error(err)
   return callback(result)
)

I invoked link this:

# using Meteor
#data = Meteor._wrapAsync(getAccounts)()


#using Future
waiter = Future.wrap(getAccounts)()
data = waiter.wait()
this.response.writeHead 200, {'Content-Type': 'text/plain'}
this.response.end JSON.stringify(data)

thanks all.


Solution

  • Just today I struggled with this very issue. The answer, it seems to me, is that meteor (or at least the iron router) doesn't handle async calls the way you'd expect. The solution is to wrap the async call into a fiber, which is the mechanism meteor uses to keep the programming synchronous.

    In your case try this (sorry, I don't know coffeescript very well):

    var Future = Npm.require('fibers/future');
    ...
    var waiter = Future.wrap(fs.readFile);
    var data = waiter('/tmp/a.txt').wait();
    response.writeHead(200, {'Content-Type': 'text/plain'})
    response.end(data)
    

    EDIT Addressing the addition to the question.

    Functions wrapped in a future need to have a callback as their last argument that has the (err, result) signature. Try this:

    @getAccounts = (callback) ->
     query = "SELECT Id, Name, AccountNumber FROM Account"
     queryBySoql(query, (err, result)->
       if err
         return console.error(err)
       return callback(err, result)
    )