admin管理员组

文章数量:1291047

To optimize the response delay, it is necessary to perform work after are response has been sent back to the client. However, the only way I can seem to get code to run after the response is sent is by using setTimeout. Is there a better way? Perhaps somewhere to plug in code after the response is sent, or somewhere to run code asynchronously?

Here's some code.

koa                  = require 'koa'
router               = require 'koa-router'

app = koa()

# routing
app.use router app

app
  .get '/mypath', (next) ->
    # ...
    console.log 'Sending response'

    yield next

    # send response???

    console.log 'Do some more work that the response shouldn\'t wait for'

To optimize the response delay, it is necessary to perform work after are response has been sent back to the client. However, the only way I can seem to get code to run after the response is sent is by using setTimeout. Is there a better way? Perhaps somewhere to plug in code after the response is sent, or somewhere to run code asynchronously?

Here's some code.

koa                  = require 'koa'
router               = require 'koa-router'

app = koa()

# routing
app.use router app

app
  .get '/mypath', (next) ->
    # ...
    console.log 'Sending response'

    yield next

    # send response???

    console.log 'Do some more work that the response shouldn\'t wait for'
Share Improve this question edited Dec 7, 2014 at 17:03 ProgramFOX 6,39011 gold badges48 silver badges54 bronze badges asked Oct 22, 2014 at 20:02 GarrettGarrett 11.7k20 gold badges86 silver badges129 bronze badges 6
  • How do you send the request? There are usually success callbacks for this kind of thing – Brennan Commented Oct 22, 2014 at 20:04
  • 2 Please show us your code. Usually, you'd just call response.end() to send/flush the response, and continue with your work. – Bergi Commented Oct 22, 2014 at 20:46
  • @Bergi response.end() isn't defined: github./koajs/koa/blob/master/docs/api/response.md. I use koa-router. I'll add some code. – Garrett Commented Oct 24, 2014 at 5:11
  • Thanks. But isn't that Coffeescript, not ES6? – Bergi Commented Oct 24, 2014 at 5:21
  • Coffeescript in ES6. I just forgot to yield. Will fix. Also, response.end() isnt supported by koa: github./koajs/koa/blob/master/docs/api/context.md#ctxres – Garrett Commented Oct 24, 2014 at 5:21
 |  Show 1 more ment

4 Answers 4

Reset to default 8

Do NOT call ctx.res.end(), it is hacky and circumvents koa's response/middleware mechanism, which means you might aswell just use express. Here is the proper solution, which I also posted to https://github./koajs/koa/issues/474#issuement-153394277

app.use(function *(next) {
  // execute next middleware
  yield next
  // note that this promise is NOT yielded so it doesn't delay the response
  // this means this middleware will return before the async operation is finished
  // because of that, you also will not get a 500 if an error occurs, so better log it manually.
  db.queryAsync('INSERT INTO bodies (?)', ['body']).catch(console.log)
})
app.use(function *() {
  this.body = 'Hello World'
})

No need for ctx.end()
So in short, do

function *process(next) {
  yield next;
  processData(this.request.body);
}

NOT

function *process(next) {
  yield next;
  yield processData(this.request.body);
}

I have the same problem.

koa will end response only when all middleware finish(In application.js, respond is a response middleware, it end the response.)

app.callback = function(){
  var mw = [respond].concat(this.middleware);
  var gen = pose(mw);
  var fn = co.wrap(gen);
  var self = this;

  if (!this.listeners('error').length) this.on('error', this.onerror);

  return function(req, res){
    res.statusCode = 404;
    var ctx = self.createContext(req, res);
    onFinished(res, ctx.onerror);
    fn.call(ctx).catch(ctx.onerror);
  }
};

But, we can make problem solved by calling response.end function which is node's api:

exports.endResponseEarly = function*(next){
    var res = this.res;
    var body = this.body;

    if(res && body){
        body = JSON.stringify(body);
        this.length = Buffer.byteLength(body);
        res.end(body);
    }

    yield* next;
};

If the generator solution doesn't work for you (e.g. you can't use generators, can't transition all of your app to using generators, you're running serverless, etc), you can call your task from a setImmediate instead, e.g.

app.use((ctx, next) => {
  await next();

  setImmediate(() => {
    void processData(ctx.request.body);
  });
});

async function processData() {
  // your async task here
}

setImmediate just puts your function directly onto the event queue, which node will then fire as soon as it can (in most cases, this is effectively immediate). This allows the main middleware function to return as normal, as you're not blocking / interrupting the control flow at all.

I'd imagine this probably works a bit better on serverless too, because they typically handle the timer functions correctly, although I haven't personally tested it.

you can run code in async task by use setTimeout, just like:

 exports.invoke = function*() {
  setTimeout(function(){
    co(function*(){
      yield doSomeTask();
    });
  },100);
  this.body = 'ok';
};

本文标签: javascriptRunning code AFTER the response has been sent by KoaStack Overflow