最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Using Callbacks With nodejs in KOA - Stack Overflow

programmeradmin6浏览0评论

Recently I work on a new project and this project use JavaScript callbacks in nodejs. Now we use KOA but the problem happens when we try to use ES6 Generators and callbacks.

//Calback function
function load(callback){
  result = null;
  //Do something with xmla4js and ajax
  callback(result);
  return result;
}

Now in KOA I need to call load and response json to client so i use this code below :

router= require('koa-router');
app = koa();
app.use(router(app));

app.get('load',loadjson);

function *loadJson(){
  var that = this;
  load(function(result){
    that.body = result;
  });
}

but i get this error :

_http_outgoing.js:331
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:331:11)
at Object.module.exports.set (G:\NAP\node_modules\koa\lib\response.js:396:16)
at Object.length (G:\NAP\node_modules\koa\lib\response.js:178:10)
at Object.body (G:\NAP\node_modules\koa\lib\response.js:149:19)
at Object.body (G:\NAP\node_modules\koa\node_modules\delegates\index.js:91:31)
at G:\NAP\Server\OlapServer\index.js:40:19
at G:\NAP\Server\OlapServer\OLAPSchemaProvider.js:1599:9
at _LoadCubes.xmlaRequest.success   (G:\NAP\Server\OlapServer\OLAPSchemaProvider.js:1107:13)
at Object.Xmla._requestSuccess (G:\NAP\node_modules\xmla4js\src\Xmla.js:2113:50)
at Object.ajaxOptionsplete (G:\NAP\node_modules\xmla4js\src\Xmla.js:2024:34)

Recently I work on a new project and this project use JavaScript callbacks in nodejs. Now we use KOA but the problem happens when we try to use ES6 Generators and callbacks.

//Calback function
function load(callback){
  result = null;
  //Do something with xmla4js and ajax
  callback(result);
  return result;
}

Now in KOA I need to call load and response json to client so i use this code below :

router= require('koa-router');
app = koa();
app.use(router(app));

app.get('load',loadjson);

function *loadJson(){
  var that = this;
  load(function(result){
    that.body = result;
  });
}

but i get this error :

_http_outgoing.js:331
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:331:11)
at Object.module.exports.set (G:\NAP\node_modules\koa\lib\response.js:396:16)
at Object.length (G:\NAP\node_modules\koa\lib\response.js:178:10)
at Object.body (G:\NAP\node_modules\koa\lib\response.js:149:19)
at Object.body (G:\NAP\node_modules\koa\node_modules\delegates\index.js:91:31)
at G:\NAP\Server\OlapServer\index.js:40:19
at G:\NAP\Server\OlapServer\OLAPSchemaProvider.js:1599:9
at _LoadCubes.xmlaRequest.success   (G:\NAP\Server\OlapServer\OLAPSchemaProvider.js:1107:13)
at Object.Xmla._requestSuccess (G:\NAP\node_modules\xmla4js\src\Xmla.js:2113:50)
at Object.ajaxOptions.plete (G:\NAP\node_modules\xmla4js\src\Xmla.js:2024:34)
Share Improve this question asked Feb 27, 2014 at 5:28 MBehtemamMBehtemam 7,91917 gold badges67 silver badges111 bronze badges 2
  • this error is because of multiple res.send(). Response is already sent and again you are trying to send . – Sumeet Kumar Yadav Commented Feb 27, 2014 at 5:40
  • in load function i need ajax because xmla4js use it. but i can log result in load only can't send to client. is this problem happen by ajax or xmla4js? – MBehtemam Commented Feb 27, 2014 at 6:08
Add a ment  | 

5 Answers 5

Reset to default 15

Just to clarify things, let's write your callback as

//Calback function
function load(callback){
    setTimeout(function() {
        var result = JSON.stringify({ 'my': 'json'});
        callback(/* error: */ null, result);
    }, 500);
}

in Koa world, this is called a thunk, meaning that it is an asynchronous function that takes only one argument: a callback with the prototype (err, res). you can check https://github./visionmedia/node-thunkify for a better explanation.

now you have to write your middleware with

function *loadJson(){
  this.type = 'application/json';
  this.body = yield load;
}

this is mainly because KOA is generator based, if your on the top of the middleware it does not support callbacks. so its not waiting for the function to finish. best solution would be to convert your function into a promise. promise works great with KOA.

I had a very similar problem using braintree (regular callbacks) and koa. Based on your code, the only change I needed to do was with the load function and how it was called.

router = require('koa-router');
app = koa();
app.use(router(app));

app.get('/load',loadjson);

function *loadJson(){
  this.body = yield load;
}

// Callback function
function load(callback) {
  // Prepare some data with xmla4js and ajax
  whatever_inputs = {...};
  final_method(whatever_inputs, callback);
}

The explanation by Jerome and Evan above is absolutely correct, and thunkify looks like a suitable process for automatically doing it.

While thunks were a nice idea, in my view a Promise is a better long-term approach. Many libraries are already moving to promises for async instead of the old node standard callback(err, data), and they're dead-simple to wrap around any async code to make a promise. Other devs will have experiences with Promises and naturally understand your code, while most would have to look up what a "thunk" is.

e.g. here I am wrapping the not-yet-promise-based jsdom up in a promise, so I can yield it in my koa generator.

const jsdom = require('node-jsdom');
const koa = require('koa');
const app = koa();
​
app.use(function *() {
  this.body = yield new Promise((resolve, reject) => jsdom.env({
    url: `http://example${this.url}`,
    done(errors, { document }) {
      if (errors) reject(errors.message);
      resolve(`<html>${document.body.outerHTML}</html>`);
    },
  }));
});
​
app.listen(2112);

Semantically, promises and generators go hand-in-hand to really clarify async code. A generator can be re-entered many times and yield several values, while a promise means "I promise I'll have some data for you later". Combined, you get one of the most useful things about Koa: the ability to yield both promises and synchronous values.

edit: here's your original example wrapped with a Promise to return:

const router = require('koa-router');
const { load } = require('some-other-lib');
const app = koa();
app.use(router(app));

app.get('load', loadjson);

function* loadJson() {
  this.body = yield new Promise(resolve => {
    load(result => resolve(result));
  });
}

To bypass Koa's built-in response handling, you may explicitly set this.respond = false;. Use this if you want to write to the raw res object instead of letting Koa handle the response for you.

Header is already written by built-in response handling before your callback is invoked.

发布评论

评论列表(0)

  1. 暂无评论