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);
}
コアの世界では、これを thunk
, これは、引数を 1 つだけ取る非同期関数であることを意味します。プロトタイプ (err, res) を使用したコールバック。確認してもいい https://github.com/visionmedia/node-thunkify より良い説明のために。
ここでミドルウェアを作成する必要があります
function *loadJson(){
this.type = 'application/json';
this.body = yield load;
}
他のヒント
これは主に、KOA がジェネレーター ベースであり、ミドルウェアの最上部でコールバックをサポートしていないためです。したがって、関数が終了するのを待ちません。最善の解決策は、関数を Promise に変換することです。Promise は KOA とうまく連携します。
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);
}
上記のジェロームとエヴァンの説明は完全に正しいです。 サンクファイ それを自動的に行うのに適したプロセスのように見えます。
「サンク」は良いアイデアですが、私の意見では、 Promise
長期的なアプローチとしては優れています。多くのライブラリはすでに、古いノード標準ではなく非同期の Promise に移行しています。 callback(err, data)
, そして、非同期コードをラップして約束するのは非常に簡単です。他の開発者は Promise の経験があり、自然にコードを理解するでしょうが、ほとんどの開発者は「サンク」とは何かを調べる必要があります。
例えばここでは、まだ 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 とジェネレーターは連携して非同期コードを明確にします。ジェネレーターは何度も再入力して複数の値を生成できますが、promise は「後でデータを用意することを約束します」という意味です。これらを組み合わせると、Koa について最も便利な機能の 1 つが得られます。Promise と同期値の両方を生成する機能。
編集:これは、返す 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
;。rawに書き込みたい場合はこれを使用してくださいres
Koa に応答を処理させるのではなく、オブジェクトを生成します。
ヘッダーは、コールバックが呼び出される前に、組み込みの応答処理によってすでに書き込まれています。