【问题标题】:JavaScript - override a function with a function that contains async callbacks and still return original valueJavaScript - 使用包含异步回调的函数覆盖函数并仍返回原始值
【发布时间】:2013-07-28 22:18:10
【问题描述】:

在 JavaScript 中,我想覆盖对象上的函数,但仍然调用原始函数并返回它的值。所以我通常会做这样的事情:

var render = res.render;

res.render = function() {
    doSomethingNew();
    return render.apply(this, arguments);
};

但是,如果该覆盖包含需要在调用原始函数之前首先触发的异步回调,例如:

var render = res.render;

res.render = function() {
    var self = this;
    var args = arguments;

    var middlewares = getSomeMiddleware();

    return callNextMiddleware(middlewares, function() {
        return render.apply(self, args);
    });
};

function callNextMiddleware(middlewares, callback) {
    var middlewareFunc = middlewares.shift();

    if (middlewareFunc) {
        return middlewareFunc.call(function() {
            return callNextMiddleware(middlewares, callback);
        });
    }
    else {
        return callback();
    }
}

请注意,我在需要时使用了“return”语句。我有一个问题,“中间件”变量是一个函数数组,每个中间件函数看起来像这样:

function myMiddleware(next) {
    performLongRunningAsyncDataAccess(function() {
        next();
    });
}

因为它不使用'return next()',所以原始 res.render 方法的返回值永远不会被传回。如果我让所有中间件函数使用'return next()',我可以让它工作,但它们来自外部源,所以我无法控制它们,我只能保证它们会调用'next()' .

一点背景知识,这是一个 Node.js 应用程序。中间件基本上是Connect中间件,我正在尝试覆盖Express.js的res.render方法。

【问题讨论】:

  • 这些函数中的哪一个在调用异步的东西?顺便说一句:我认为应该是return middlewareFunc.call(this, function()...)
  • basilikum,中间件函数本身可能会做一些异步的事情,例如从数据库中选择一些记录,或点击文件系统等。
  • 好的,它到底应该做什么?就像现在一样,您似乎想遍历所有中间件函数并在最后调用渲染函数?或者你可能先调用render,得到一堆中间件函数,全部执行,然后再调用render?
  • 同意@basilikum。在这段代码中似乎没有什么是异步的。回调!=异步。
  • 我已经更新了示例中间件。希望它更清楚

标签: javascript node.js asynchronous callback


【解决方案1】:

通常,将异步函数与return 语句混合使用是个坏主意。您想要返回的所有内容都可以作为参数传递给回调函数。所以我仍然希望我能正确理解你的代码,但我假设你调用了render 函数,然后它会获取一组middleware 函数。然后你想执行该数组中的所有函数,使用下一个作为前一个的回调。执行完所有函数后,应再次调用 render 函数,从而创建一种无限循环。假设所有这些,让我们看看您的一些return 声明:

return middlewareFunc.call(function() {
    return callNextMiddleware(middlewares, callback);
});

此块中的第一个return 是无用的,因为middlewareFunc 是异步的,因此很可能会返回undefined。第二个return 语句也没有用,因为它从函数返回,您将其用作回调。但是由于您的回调只是使用next(); 调用的,因此永远不会使用返回值。

else {
    return callback();
}

在这个块中callbackrender 函数。那么让我们来看看这个函数:

res.render = function() {
    var self = this;
    var args = arguments;

    var middlewares = getSomeMiddleware();

    return callNextMiddleware(middlewares, function() {
        return render.apply(self, args);
    });
};

所以最后三个return 语句基本上都在那里,因为你想从你的render 函数返回一些东西。但为了保持一致,您也应该考虑为该函数使用回调:

res.render = function(callback) {
    var self = this;
    var args = arguments;

    var middlewares = getSomeMiddleware();

    callNextMiddleware(middlewares, function() {
        //this will be called after all the middleware function have been executed
        callback();
        render.apply(self, args);
    });
};

所以基本上你摆脱了所有return 语句并使用纯异步设计模式。

【讨论】:

    【解决方案2】:

    callNextMiddleware 应该返回其递归调用的返回值,而不是 middlewareFunc 的。

    if (middlewareFunc) {
        var result;
        middlewareFunc.call(this, function() {
            result = callNextMiddleware(middlewares, callback);
        });
        return result;
    }
    

    小提琴:http://jsfiddle.net/mWGXs

    【讨论】:

    • 如果中间件执行了一些需要很长时间的异步操作(例如执行数据库操作),则 'result' 返回 undefined。我已经更新了您的示例 jsfiddle.net/mWGXs/1
    • 现已清除,但我认为没有解决方案。归档目标render() 应该等到所有回调完成后才返回,但是在 JavaScript 中没有办法。
    • 谢谢 Mics,我想可能是这样。看来我得另辟蹊径了。
    猜你喜欢
    • 1970-01-01
    • 2019-01-27
    • 2023-04-04
    • 1970-01-01
    • 2018-01-03
    • 1970-01-01
    • 2019-02-27
    • 2012-05-12
    • 2014-10-03
    相关资源
    最近更新 更多