Utilisation des rappels avec nodejs dans KOA
-
23-12-2019 - |
Question
Récemment, je travaille sur un nouveau projet et ce projet utilise des rappels JavaScript dans nodejs.Maintenant, nous utilisons KOA mais le problème survient lorsque nous essayons d'utiliser les générateurs et les rappels ES6.
//Calback function
function load(callback){
result = null;
//Do something with xmla4js and ajax
callback(result);
return result;
}
Maintenant en KOA je dois appeler load
et réponse json au client donc j'utilise ce code ci-dessous :
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;
});
}
mais j'obtiens cette erreur :
_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)
La solution
Juste pour clarifier les choses, écrivons votre rappel comme
//Calback function
function load(callback){
setTimeout(function() {
var result = JSON.stringify({ 'my': 'json'});
callback(/* error: */ null, result);
}, 500);
}
dans le monde Koa, cela s'appelle un thunk
, ce qui signifie qu'il s'agit d'une fonction asynchrone qui ne prend qu'un seul argument :un rappel avec le prototype (err, res).tu peux vérifier https://github.com/visionmedia/node-thunkify pour une meilleure explication.
maintenant vous devez écrire votre middleware avec
function *loadJson(){
this.type = 'application/json';
this.body = yield load;
}
Autres conseils
Ceci est principalement parce que KOA est basé sur le générateur, si vous êtes au sommet du middleware, cela ne prend pas en charge les rappels.Donc, il n'attend pas que la fonction finisse.La meilleure solution serait de convertir votre fonction en une promesse.Promesse fonctionne bien avec KOA.
J'ai eu un problème très similaire en utilisant Braintree (Callbacks réguliers) et KOA.Basé sur votre code, le seul changement que je devais faire était avec la fonction de charge et comment elle a été appelée.
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);
}
L'explication de Jerome et Evan ci-dessus est absolument correcte et Thunkify ressemble à un processus appropriépour le faire automatiquement.
Même si les remerciements étaient une bonne idée, à mon avis, Promise
est une meilleure approche à long terme.De nombreuses bibliothèques adoptent déjà des promesses asynchrones au lieu de l'ancien standard de nœud. callback(err, data)
, et ils sont très simples à envelopper n'importe quel code asynchrone pour faire une promesse.D'autres développeurs auront des expériences avec Promises et comprendront naturellement votre code, tandis que la plupart devront rechercher ce qu'est un "thunk".
par exemple.ici, j'enveloppe le jsdom pas encore basé sur une promesse dans une promesse, afin que je puisse le produire dans mon générateur de 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);
Sémantiquement, les promesses et les générateurs vont de pair pour vraiment clarifier le code asynchrone.Un générateur peut être saisi plusieurs fois et donner plusieurs valeurs, tandis qu'une promesse signifie "Je promets que j'aurai des données pour vous plus tard".Ensemble, vous obtenez l’une des choses les plus utiles à propos de Koa :la capacité de produire à la fois des promesses et des valeurs synchrones.
modifier:voici votre exemple original accompagné d'une promesse de retour :
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));
});
}
Pour contourner la gestion des réponses intégrée de Koa, vous pouvez définir explicitement
this.respond = false
;.Utilisez-le si vous souhaitez écrire dans le fichier brutres
object au lieu de laisser Koa gérer la réponse à votre place.
L'en-tête est déjà écrit par la gestion de réponse intégrée avant que votre rappel ne soit invoqué.