简体   繁体   中英

Add an extra parameter to a callback function in Javascript

Hello Stackoverflow users,

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. One of the APIs I communicate with has an SDK that works synchronically. 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. When the server API layer receives the request data, it passes it to a third-party service function ( .create(...) ). The third-party service function receives a callback with 2 parameters 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.

** 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.

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.

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?

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):

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 .

With modern javascript it is often easier to handle this with Promise s. Convert legacy functions to return a Promise and then you can use 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:

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.
        });
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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