【问题标题】:Using Callbacks With nodejs in KOA在 KOA 中使用带有 nodejs 的回调
【发布时间】:2014-03-30 08:47:40
【问题描述】:

最近我在一个新项目上工作,这个项目在 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)

【问题讨论】:

  • 这个错误是因为多个 res.send()。响应已发送,您再次尝试发送。
  • 在加载函数中我需要 ajax,因为 xmla4js 使用它。但我可以记录导致负载只能发送到客户端。这个问题是 ajax 还是 xmla4js 发生的?

标签: javascript node.js callback koa


【解决方案1】:

为了澄清一些事情,让我们把你的回调写成

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

【讨论】:

    【解决方案2】:

    这主要是因为 KOA 是基于生成器的,如果你在中间件的顶部它不支持回调。所以它不等待功能完成。最好的解决方案是将您的功能转换为承诺。 promise 与 KOA 配合得很好。

    【讨论】:

    • 你能提供一个与问题相关的承诺示例吗?
    • 您可以使用上面的示例。选择的答案。或者,如果您愿意,可以使用 co
    【解决方案3】:

    我在使用 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 看起来是一个适合自动执行的过程。

    【讨论】:

      【解决方案4】:

      虽然 thunk 是个好主意,但在我看来,Promise 是一种更好的长期方法。许多库已经转向异步的承诺,而不是旧的节点标准callback(err, data),并且它们非常简单地包装任何异步代码来做出承诺。其他开发人员将具有 Promises 的经验并自然理解您的代码,而大多数开发人员则必须查找“thunk”是什么。

      例如在这里,我将基于 not-yet-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 和 generator 齐头并进,以真正阐明异步代码。生成器可以多次重新输入并产生多个值,而 promise 表示“我保证稍后会为您提供一些数据”。结合起来,您将获得 Koa 最有用的东西之一:产生 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));
        });
      }
      

      【讨论】:

        【解决方案5】:

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

        在调用回调之前,内置响应处理已经编写了标头。

        【讨论】:

        • 请注意,this.respond 不被 Koa 官方支持,并且会破坏 Koa 应用程序。
        • 我真的不认为这是对这个问题的回答。它更适合作为其他地方的评论。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-09
        • 1970-01-01
        相关资源
        最近更新 更多