Hola usuarios de Stackoverflow,
Mucha gente como yo buscó cómo pasar argumentos adicionales a una función de devolución de llamada. Las preguntas tienen títulos similares pero en realidad tienen diferentes desafíos y muchas formas de resolver. Además, siempre es un placer compartir prácticas para tener más experiencia.
Recientemente, enfrenté un desafío bastante simple en mi proyecto de nodo js. Una de las API con las que me comunico tiene un SDK que funciona sincrónicamente. Y solía pasar funciones de devolución de llamada cada vez (lo cual es molesto cuando tienes solicitudes que dependen unas de otras y algunos datos deben transferirse dentro de las capas de la aplicación).
Imagine un flujo de pago del plan que es así, un cliente envía una solicitud al servidor que incluye el plan seleccionado y su identificación. Cuando la capa de la API del servidor recibe los datos de la solicitud, los pasa a una función de servicio de terceros ( .create(...) ). La función de servicio de terceros recibe una devolución de llamada con 2 parámetros function(err, plan_document) . Y luego, se supone que la devolución de llamada aplica la lógica del plan seleccionado en el cliente por la ID en la solicitud.
** Necesitamos pasar los datos del cliente y del plan a la función de devolución de llamada para aplicar la lógica. El servicio de terceros proporciona a la devolución de llamada un parámetro plan_document y aún necesitamos pasar de alguna manera la identificación del cliente de la capa API al servicio.
El código se verá así.
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); };------------------ EDITAR ------------------
Me pregunto qué pasa si el flujo fue más largo y necesito la identificación del cliente más allá de la función de actualización como esta.
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); }; ¿Qué debo hacer en este caso? ¿Debería seguir repitiendo la .bind({'client_id': client_id}) hasta el último paso del flujo?
Si desea ayudar a las personas mayores, puede vincular fácilmente mediante una devolución de llamada contenedora, como esta:
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); };La forma tradicional es utilizar un cierre. Defina las funciones dentro del alcance de los padres para que puedan acceder a client_id como una variable adjunta (algo así como variables globales):
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); }; Los cierres son para los ámbitos lo que los objetos son para las clases. Un cierre es una instancia de un alcance. Entonces, a diferencia de la variable global regular, cada llamada a create_plan_agreement() creará su propio cierre con su propia copia de client_id .
Con javascript moderno, a menudo es más fácil manejar esto con Promise s. Convierta las funciones heredadas para devolver una Promise y luego puede usar 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); }; O incluso sin async/await Promise todavía hace que las devoluciones de llamada sean más fáciles de usar:
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. }); };