una then termine que promesas programacion pagina funcion esperar ejecutar cargar asincronia asincrona antes anidadas javascript asynchronous callback synchronous

then - promesas javascript



Javascript-sincronización después de llamadas asíncronas (6)

A menos que esté dispuesto a serializar el AJAX, no hay otra manera en que pueda pensar para hacer lo que está proponiendo. Dicho esto, creo que lo que tienes es bastante bueno, pero es posible que quieras limpiar un poco la estructura para no ensuciar el objeto que estás creando con los datos de inicialización.

Aquí hay una función que podría ayudarte:

function gate(fn, number_of_calls_before_opening) { return function() { arguments.callee._call_count = (arguments.callee._call_count || 0) + 1; if (arguments.callee._call_count >= number_of_calls_before_opening) fn.apply(null, arguments); }; }

Esta función es lo que se conoce como una función de orden superior, una función que toma funciones como argumentos. Esta función particular devuelve una función que llama a la función pasada cuando ha sido llamada number_of_calls_before_opening times. Por ejemplo:

var f = gate(function(arg) { alert(arg); }, 2); f(''hello''); f(''world''); // An alert will popup for this call.

Puede usar esto como su método de devolución de llamada:

foo.bar = function() { var callback = gate(this.method, 2); sendAjax(new Request(), callback); sendAjax(new Request(), callback); }

La segunda devolución de llamada, cualquiera que sea, asegurará que se llame al method . Pero esto lleva a otro problema: la función de gate llama a la función pasada sin ningún contexto, lo que significa que se referirá al objeto global, no al objeto que está construyendo. Hay varias formas de evitar esto: puede cerrar this aliasing o self . O puede crear otra función de orden superior que haga precisamente eso.

Así es como se vería el primer caso:

foo.bar = function() { var me = this; var callback = gate(function(a,b,c) { me.method(a,b,c); }, 2); sendAjax(new Request(), callback); sendAjax(new Request(), callback); }

En este último caso, la otra función de orden superior sería algo como lo siguiente:

function bind_context(context, fn) { return function() { return fn.apply(context, arguments); }; }

Esta función devuelve una función que llama a la función pasada en el contexto pasado. Un ejemplo de esto sería el siguiente:

var obj = {}; var func = function(name) { this.name = name; }; var method = bind_context(obj, func); method(''Your Name!''); alert(obj.name); // Your Name!

Para ponerlo en perspectiva, su código se verá de la siguiente manera:

foo.bar = function() { var callback = gate(bind_context(this, this.method), 2); sendAjax(new Request(), callback); sendAjax(new Request(), callback); }

En cualquier caso, una vez que haya realizado estas refactorizaciones, habrá aclarado el objeto que se está construyendo con todos sus miembros, que solo son necesarios para la inicialización.

Tengo un objeto Javascript que requiere 2 llamadas a un servidor externo para construir sus contenidos y hacer algo significativo. El objeto está construido de tal manera que la instancia de una instancia de este hará automáticamente estas 2 llamadas. Las 2 llamadas comparten una función de devolución de llamada común que opera sobre los datos devueltos y luego llama a otro método. El problema es que el siguiente método no debe invocarse hasta que ambos métodos regresen. Aquí está el código como lo he implementado actualmente:

foo.bar.Object = function() { this.currentCallbacks = 0; this.expectedCallbacks = 2; this.function1 = function() { // do stuff var me = this; foo.bar.sendRequest(new RequestObject, function(resp) { me.commonCallback(resp); }); }; this.function2 = function() { // do stuff var me = this; foo.bar.sendRequest(new RequestObject, function(resp) { me.commonCallback(resp); }); }; this.commonCallback = function(resp) { this.currentCallbacks++; // do stuff if (this.currentCallbacks == this.expectedCallbacks) { // call new method } }; this.function1(); this.function2(); }

Como puede ver, estoy forzando que el objeto continúe después de que ambas llamadas hayan regresado usando un simple contador para validar que ambas han regresado. Esto funciona, pero parece una implementación realmente pobre. Solo he trabajado con Javascript durante algunas semanas y me pregunto si existe un método mejor para hacer lo mismo que todavía tengo que encontrar.

Gracias por cualquier y toda la ayuda.


Apenas hay otra manera de tener este contador. Otra opción sería usar un objeto {} y agregar una clave para cada solicitud y eliminarla si se completa. De esta forma sabría de inmediato qué ha regresado. Pero la solución permanece igual.

Puedes cambiar el código un poco. Si es como en su ejemplo que solo necesita llamar a otra función dentro de CommonCallback (lo llamé otherFunction) entonces no necesita el CommonCallback. Para guardar el contexto, ya usaste cierres. En lugar de

foo.bar.sendRequest(new RequestObject, function(resp) { me.commonCallback(resp); });

podrías hacerlo de esta manera

foo.bar.sendRequest(new RequestObject, function(resp) { --me.expectedCallbacks || me.otherFunction(resp); });


Compartí la misma frustración. Mientras encadenaba más llamadas asincrónicas, se convirtió en un infierno de devolución de llamada. Entonces, se me ocurrió mi propia solución. Estoy seguro de que hay soluciones similares, pero quería crear algo muy simple y fácil de usar. Asynq es un script que escribí para encadenar tareas asincrónicas. Entonces, para ejecutar f2 después de f1, puedes hacer:

asynq.run (f1, f2)

Puede encadenar tantas funciones como desee. También puede especificar parámetros o ejecutar una serie de tareas en elementos de una matriz. Espero que esta biblioteca pueda resolver sus problemas o problemas similares que están teniendo otros.


Eso es algo bueno, Sr. Kyle.

Para decirlo un poco más simple, suelo usar una función de inicio y una función de finalización.
-La función de inicio toma una lista de funciones que se ejecutarán.
-La función Done se llama por las devoluciones de llamada de sus funciones que pasó al método de inicio.
-Además, puede pasar una función o lista de funciones al método hecho que se ejecutará cuando se complete la última devolución de llamada.

Las declaraciones se ven así.

var PendingRequests = 0; function Start(Requests) { PendingRequests = Requests.length; for (var i = 0; i < Requests.length; i++) Requests[i](); }; //Called when async responses complete. function Done(CompletedEvents) { PendingRequests--; if (PendingRequests == 0) { for (var i = 0; i < CompletedEvents.length; i++) CompletedEvents[i](); } }

Aquí hay un ejemplo simple usando la API de Google Maps.

//Variables var originAddress = "*Some address/zip code here*"; //Location A var formattedAddress; //Formatted address of Location B var distance; //Distance between A and B var location; //Location B //This is the start function above. Passing an array of two functions defined below. Start(new Array(GetPlaceDetails, GetDistances)); //This function makes a request to get detailed information on a place. //Then callsback with the **GetPlaceDetailsComplete** function function GetPlaceDetails() { var request = { reference: location.reference //Google maps reference id }; var PlacesService = new google.maps.places.PlacesService(Map); PlacesService.getDetails(request, GetPlaceDetailsComplete); } function GetPlaceDetailsComplete(place, status) { if (status == google.maps.places.PlacesServiceStatus.OK) { formattedAddress = place.formatted_address; Done(new Array(PrintDetails)); } } function GetDistances() { distService = new google.maps.DistanceMatrixService(); distService.getDistanceMatrix( { origins: originAddress, destinations: [location.geometry.location], //Location contains lat and lng travelMode: google.maps.TravelMode.DRIVING, unitSystem: google.maps.UnitSystem.IMPERIAL, avoidHighways: false, avoidTolls: false }, GetDistancesComplete); } function GetDistancesComplete(results, status) { if (status == google.maps.DistanceMatrixStatus.OK) { distance = results[0].distance.text; Done(new Array(PrintDetails)); } } function PrintDetails() { alert(*Whatever you feel like printing.*); }

Entonces, en pocas palabras, lo que estamos haciendo aquí es
-Pasando una serie de funciones para la función de inicio
-La función de Inicio llama a las funciones en la matriz y establece el número de Solicitudes Pendientes
-En las devoluciones de llamada para nuestras solicitudes pendientes, llamamos a la función Done -La función Done toma una serie de funciones
-La función Done disminuye el contador de Peticiones Pendientes
-Si no hay más solicitudes pendientes, llamamos a las funciones pasadas a la función Done

Es un ejemplo simple pero práctico de sincronizar llamadas web. Traté de usar un ejemplo de algo que es ampliamente utilizado, así que fui con la API de Google Maps. Espero que alguien encuentre esto útil.


Otra forma sería tener un punto de sincronización gracias a un temporizador. No es bonito, pero tiene la ventaja de no tener que agregar la llamada a la siguiente función dentro de la devolución de llamada.

Aquí la función execute_jobs es el punto de entrada. toma una lista de datos para ejecutar simultáneamente. Primero establece el número de trabajos para esperar al tamaño de la list . Luego establece un temporizador para probar la condición final (el número que cae a 0). Y finalmente envía un trabajo para cada dato. Cada trabajo reduce la cantidad de trabajos esperados en uno.

Se vería como algo así:

var g_numJobs = 0; function async_task(data) { // // ... execute the task on the data ... // // Decrease the number of jobs left to execute. --g_numJobs; } function execute_jobs(list) { // Set the number of jobs we want to wait for. g_numJobs = list.length; // Set the timer (test every 50ms). var timer = setInterval(function() { if(g_numJobs == 0) { clearInterval(timer); do_next_action(); } }, 50); // Send the jobs. for(var i = 0; i < list.length; ++i) { async_task(list[i])); } }

Para mejorar este código, puede hacer una clase de Job y JobList . El Job ejecutaría una devolución de llamada y disminuiría el número de trabajos pendientes, mientras que la JobList trabajos JobList el temporizador y llamaría a la devolución de llamada una vez que los trabajos hayan finalizado.


Puedo agregar que Underscore.js tiene un buen ayudante para esto :

Crea una versión de la función que solo se ejecutará después de que se le llame primero count count. Útil para agrupar respuestas asincrónicas, donde desea asegurarse de que todas las llamadas asíncronas hayan terminado, antes de continuar .

_.after(count, function)

El código para _after (as-of versión 1.5.0):

_.after = function(times, func) { return function() { if (--times < 1) { return func.apply(this, arguments); } }; };

La información de la licencia (a partir de la versión 1.5.0)