简体   繁体   English

在 Javascript 中为回调函数添加一个额外的参数

[英]Add an extra parameter to a callback function in Javascript

Hello Stackoverflow users,您好 Stackoverflow 用户,

Many peoples like me searched for how to pass extra arguments to a callback function.许多像我这样的人都在搜索如何将额外的参数传递给回调函数。 The questions have similar titles but actually they have different challenges and many ways to solve.这些问题的标题相似,但实际上它们具有不同的挑战和许多解决方法。 Plus, it is always a pleasure to share practices to be more experienced.另外,分享实践以增加经验总是很高兴。

Recently, I faced a pretty simple challenge in my node js project.最近,我在我的 node js 项目中遇到了一个非常简单的挑战。 One of the APIs I communicate with has an SDK that works synchronically.我与之通信的其中一个 API 有一个同步工作的 SDK。 And I used to pass callback functions every time (which is annoying when you have requests depending on each other and some data needs to transfer within the app layers).而且我曾经每次都传递回调函数(当您有相互依赖的请求并且需要在应用程序层内传输一些数据时,这很烦人)。

Imagine a plan payment flow that goes like this, a client sends a request to the server including the selected plan and his ID.想象一下这样的计划支付流程,客户端向服务器发送一个请求,其中包括所选计划和他的 ID。 When the server API layer receives the request data, it passes it to a third-party service function ( .create(...) ).服务器 API 层收到请求数据后,会将其传递给第三方服务函数( .create(...) )。 The third-party service function receives a callback with 2 parameters function(err, plan_document) .第三方服务函数接收带有 2 个参数function(err, plan_document)的回调。 And then, the callback is supposed to apply the selected plan logic on the client by the ID in the request.然后,回调应该通过请求中的 ID 将选定的计划逻辑应用于客户端。

** We need to pass the client's and the plan's data to the callback function to apply the logic. ** 我们需要将客户端和计划的数据传递给回调函数以应用逻辑。 The third-party service provides to the callback a plan_document parameter and we still need to somehow pass the client id from the API layer to the service.第三方服务为回调提供了一个 plan_document 参数,我们仍然需要以某种方式将客户端 ID 从 API 层传递给服务。

The code will look like this.代码将如下所示。

const create_plan_agreement = (req, res) => {
    // some code
    var client_id = req.auth.client_id;
    third_party.plan_agreement.create({}, update_plan_agreement);
};
const update_plan_agreement = (err, plan_document, client_id) => {
    /* 
        The third-party `third_party.plan_agreement.create` function passes the first 
        two parameters and somehow we need to add the client_id 
    */
    console.log('client plan activated');
    active_client_plan(plan_document, client_id);
};

------------------ EDIT ------------------ - - - - - - - - - 编辑 - - - - - - - - -

I wonder what if the flow was longer and I need the client id farther than the update function like this.我想知道如果流程更长并且我需要客户端 ID 比这样的更新功能更远怎么办。

const create_plan_agreement = (req, res) => {
    // some code
    var client_id = req.auth.client_id;
    third_party.plan_agreement.create({}, update_plan_agreement);
};
const update_plan_agreement = (err, plan_document) => {
    console.log('plan activated, send notification to the client');
    third_party.plan_agreement.update(plan_document, send_agreement_notification);
};
const send_agreement_notification = (err, plan_document) => {
    console.log('client plan activated');
    active_client_plan(plan_document, this.client_id);
};

What should I do in this case?在这种情况下我该怎么办? Should I keep repeating the .bind({'client_id': client_id}) function until the last step in the flow?我是否应该一直重复.bind({'client_id': client_id})函数,直到流程的最后一步?

If you want to support older people, you can easily bind using a containing callback, like this:如果您想支持老年人,您可以使用包含回调轻松绑定,如下所示:

 const create_plan_agreement = (req, res) => { // some code var client_id = req.auth.client_id; third_party.plan_agreement.create({}, function(params, from, create) { update_plan_agreement(params, from, create, client_id) }); }; const update_plan_agreement = (err, plan_document, client_id) => { /* The third-party `third_party.plan_agreement.create` function passes the first two parameters and somehow we need to add the client_id */ console.log('client plan activated'); active_client_plan(plan_document, client_id); };

The traditional way is to use a closure.传统的方法是使用闭包。 Define the functions inside the parent's scope so that they can access the client_id as an enclosed variable (kind of like global variables):在父范围内定义函数,以便它们可以将client_id作为封闭变量(有点像全局变量)访问:

const create_plan_agreement = (req, res) => {
    // some code
    var client_id = req.auth.client_id;


    const update_plan_agreement = (err, plan_document) => {
        console.log('plan activated, send notification to the client');
        third_party.plan_agreement.update(plan_document, send_agreement_notification);
    };

    const send_agreement_notification = (err, plan_document) => {
        console.log('client plan activated');

        // Note: this function can access client_id
        // because it is in scope
        active_client_plan(plan_document, client_id);
    };

    third_party.plan_agreement.create({}, update_plan_agreement);
};

Closures are to scopes what objects are to classes.闭包之于作用域就像对象之于类。 A closure is an instance of a scope.闭包是作用域的一个实例 So unlike regular global variable, each call to create_plan_agreement() will create its own closure with its own copy of client_id .因此,与常规全局变量不同,每次调用create_plan_agreement()都会使用自己的client_id副本创建自己的闭包。

With modern javascript it is often easier to handle this with Promise s.使用现代 javascript,通常使用Promise更容易处理这个问题。 Convert legacy functions to return a Promise and then you can use async/await :转换遗留函数以返回Promise ,然后您可以使用async/await

const create_plan_agreement = async (req, res) => {
    // some code
    var client_id = req.auth.client_id;
    try {
        var plan_document = await plan_agreement_create({});
        var updated_plan_document = await update_plan_agreement(plan_document);
        send_agreement_notification(updated_plan_document, client_id);
    }
    catch (err) {
        // handle errors here.
    }
};

const plan_agreement_create = (arg) {
    return new Promise ((ok, fail) => {
        third_party.plan_agreement.create({}, (err, result) => {
            if (err) {
                return fail(err);
            }
            ok(result);
        });
    })
}

const update_plan_agreement = (plan_document) => {
    return new Promise ((ok, fail) => {
        third_party.plan_agreement.update(plan_document, (err, result) => {
            if (err) return fail(err);
            ok(result);
        });
    });
};

const send_agreement_notification = (plan_document, client_id) => {
    active_client_plan(plan_document, client_id);
};

Or even without async/await Promise s still make callbacks easier to use:或者即使没有async/await Promise仍然使回调更易于使用:

const create_plan_agreement = async (req, res) => {
    // some code
    var client_id = req.auth.client_id;

    plan_agreement_create({})
        .then(doc => update_plan_agreement(doc));
        .then(doc => {
            send_agreement_notification(doc, client_id)
        })
        .catch(err => {
            // handle errors here.
        });
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM