2014-02-27 32 views
7

最近,我正在研究一个新项目,该项目在nodejs中使用JavaScript回调。现在我们使用KOA,但是当我们尝试使用ES6生成器和回调函数时会出现问题。在KOA中使用回调和nodejs

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

现在KOA我需要所以我用下面这段代码调用load和响应JSON客户:

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; 
    }); 
} 

,但我得到这个错误:

_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.complete (G:\NAP\node_modules\xmla4js\src\Xmla.js:2024:34) 
+0

这个错误是因为多个res.send()。响应已发送,并且您正尝试发送。 – Sumeet

+0

在加载函数中我需要ajax,因为xmla4js使用它。但我可以登录负载的结果只能发送到客户端。这个问题是由ajax还是xmla4js发生的? – MBehtemam

回答

15

只是为了澄清东西,让我们写你的回调为

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

在Koa世界中,这被称为thunk,这意味着它是一个异步函数,它只接受一个参数:一个带原型的回调函数(err,res)。你可以检查https://github.com/visionmedia/node-thunkify以获得更好的解释。

现在你必须与

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

写你的中间件要绕过兴亚内置的响应处理,你可以明确地设置this.respond = false。如果您想写入原始对象res而不是让Koa为您处理响应,请使用此选项。

在您的回调被调用之前,标题已经由内置响应处理写入。

+0

请注意''this.respond'没有得到官方的Koa支持,并且打破了Koa的申请。 –

+0

我并不认为这是对问题的回答。作为其他地方的评论更合适。 –

1

这主要是因为KOA是基于生成器的,如果你在中间件的顶部它不支持回调。所以它不等待功能完成。最好的解决方案是将您的功能转换为承诺。诺言与KOA合作很好。

+2

你能提供一个关于这个问题的承诺的例子吗? – depperm

+0

你可以使用上面的例子。选择的答案。或者如果你喜欢,你可以使用'co'库 –

0

我有一个非常类似的问题,使用braintree(常规回调)和koa。根据你的代码,我需要做的唯一的改变是使用load函数以及它的调用方式。

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); 
} 

杰罗姆和埃文上面的解释是绝对正确的,并thunkify看起来像自动做一个合适的方法。

0

虽然thunk是一个不错的主意,但我认为Promise是一个更好的长期方法。许多库已经转向承诺异步,而不是旧的节点标准callback(err, data),并且它们很简单,可以绕过任何异步代码来实现承诺。其他开发者将会有Promise的经验,并自然地理解你的代码,而大多数人将不得不查看什么是“thunk”。

例如在这里,我正在将尚未承诺的jsdom包装在承诺中,所以我可以在我的koa生成器中放弃它。

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.org${this.url}`, 
    done(errors, { document }) { 
     if (errors) reject(errors.message); 
     resolve(`<html>${document.body.outerHTML}</html>`); 
    }, 
    })); 
}); 
​ 
app.listen(2112); 

从语义上讲,承诺和生成器携手合作以真正阐明异步代码。发电机可以多次重新输入并产生多个值,而承诺意味着“我保证我稍后会为你提供一些数据”。综合起来,你会得到关于Koa最有用的事情之一:产生承诺和同步值的能力。

编辑:这里是你原来的例子并承诺包返回:

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)); 
    }); 
}