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)
Était-ce utile?

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 brut res 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é.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top