在KOA中使用callback与nodejs

最近我正在研究一个新的项目,这个项目在nodejs中使用JavaScriptcallback。 现在我们使用KOA,但是当我们尝试使用ES6生成器和callback函数时会发生问题。

//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) 

为了澄清事情,让我们把你的callback写成

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

在Koa世界中,这被称为thunk ,这意味着它是一个只有一个参数的asynchronous函数:与原型(err,res)的callback。 你可以查看https://github.com/visionmedia/node-thunkify获得更好的解释。

现在你必须写你的中间件

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

这主要是因为KOA是基于生成器的,如果你在中间件的顶部它不支持callback。 所以它不等待function完成。 最好的解决办法是把你的function转换成承诺。 诺言与KOA很好地合作。

我有一个非常类似的问题,使用braintree(常规callback)和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); } 

上面Jerome和Evan的解释是绝对正确的, thunkify看起来像是一个适合自动执行的过程。

虽然thunks是一个好主意,在我看来, Promise是一个更好的长期方法。 许多库已经转向了对asynchronous的承诺,而不是旧的节点标准callback(err, data) ,而且它们很简单,可以包装任何asynchronous代码来实现承诺。 其他开发者将会有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);  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);  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); 

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

编辑:这是你的原始示例包裹着一个Promise来返回:

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

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

在调用callback之前,头文件已经通过内置的响应处理写入了。