在 KOA 中使用 Nodejs 回调
-
23-12-2019 - |
题
最近我在做一个新项目,这个项目在nodejs中使用JavaScript回调。现在我们使用 KOA 但是当我们尝试使用 ES6 生成器和回调时就会出现问题。
//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)
解决方案
只是为了澄清事情,让我们将回调编写为
//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;
}
其他提示
这主要是因为KOA是基于生成器的,如果你在中间件之上,它不支持回调。所以它不等待函数完成。最好的解决方案是将您的函数转换为承诺。Promise 与 KOA 配合得很好。
我在使用 Braintree(定期回调)和 koa 时遇到了非常相似的问题。根据您的代码,我需要做的唯一更改是加载函数及其调用方式。
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
是一个更好的长期方法。许多库已经转向异步承诺,而不是旧的节点标准 callback(err, data)
, ,并且它们非常简单地包装任何异步代码来做出承诺。其他开发人员将拥有 Promise 的经验并自然地理解您的代码,而大多数开发人员则必须查找“thunk”是什么。
例如在这里,我将尚未基于 Promise 的 jsdom 包装在一个 Promise 中,这样我就可以在我的 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);
从语义上讲,Promise 和生成器齐头并进,真正澄清了异步代码。生成器可以多次重新输入并产生多个值,而承诺意味着“我保证稍后会为您提供一些数据”。结合起来,您可以获得 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));
});
}
要绕过 Koa 的内置响应处理,您可以显式设置
this.respond = false
;.如果您想写入原始数据,请使用此选项res
对象而不是让 Koa 为你处理响应。
在调用回调之前,标头已由内置响应处理写入。