JavaScript - 使用包含异步回调但仍返回原始值的函数覆盖函数

JavaScript - override a function with a function that contains async callbacks and still return original value

本文关键字:函数 原始 返回 覆盖 包含 回调 异步 JavaScript      更新时间:2023-09-26

在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 应用程序。中间件基本上是连接中间件,我正在尝试覆盖 Express.js res.render 方法。

通常,将异步函数与return语句混合使用是一个坏主意。你想要返回的所有内容,都可以作为参数传递给你的回调函数。所以我仍然希望我正确理解你的代码,但我假设你调用render函数,然后获取一个middleware函数数组。然后,您希望执行该数组中的所有函数,使用 next 作为对前一个函数的回调。执行完所有函数后,应再次调用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语句,并使用了纯粹的异步设计模式。

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

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

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