Como todos sabréis, hay momentos en los que necesitamos ejecutar operaciones más o menos largas, y en los que podemos tener ciertos bloqueos es nuestro cliente de AX. Esto, en las versiones antiguas de Dynamics AX podía resultar un poco molesto para el usuario final, ya que, durante estos bloqueos no podíamos hacer nada con el cliente, y al primer clic de ratón, nos aparecía el famoso «no responde…». Por este motivo y debido a que MSDyn365FO es ahora un cliente basado en tecnología web, resulta necesario tener un mayor control sobre la ejecución de estas operaciones «pesadas», ya que, de no ser así, podríamos sufrir paradas de los procesos debido a cortes en las sesiones del navegador. Esto quiere decir que, si dejamos ejecutando una operación larga en nuestro sistema, corremos el riesgo de que la sesión web finalice, y con ello, nuestra operación.
Para solventar este problema contamos con el Async Framework, que nos permitirá ejecutar operaciones en segundo plano, pudiendo así realizar cualquier otra tarea mientras finaliza la operación que tenemos en marcha, evitando así los cortes de sesión y asegurándonos que todo funciona correctamente.
La forma de ejecutar estas operaciones de forma asíncrona es, como veremos ahora mismo, bastante fácil y rápida:
Imaginemos que tenemos una clase con un método estático, el cual, como sabéis puede ser ejecutado desde cualquier punto del programa. Este método lo único que hace es lanzar un sleep por el tiempo recibido en parámetros, y lanzar un mensaje al infolog.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class JATMAsyncClass { public static void waitAndInfo(int _wait) { str text; int time = _wait / 1000; sleep(_wait); text = strFmt("Tiempo de espera: %1 segundos", time); info(text); } } |
Si este método lo ejecutamos desde un formulario, el resultado será una espera del número de milisegundos indicados por parámetros, y posteriormente nos aparecerá el mensaje en el infolog. Como veis es un método muy sencillo pero que nos va a permitir observar perfectamente el comportamiento del sistema cuando lo ejecutemos de forma asíncrona. Y para ello, lo único que tenemos que hacer es ejecutar el método runAsync existente en la clase FormRun, lo que quiere decir que es un método que podemos ejecutar desde cualquier formulario de #MSDyn365FO haciendo la llamada a element.runAsync(…). Veamos un ejemplo:
1 2 3 |
element.runAsync(classNum(JATMAsyncClass), staticMethodStr(JATMAsyncClass, waitAndInfo), [10000]); |
Como vemos, al método runAsync debemos pasarle por parámetros (como mínimo) la clase donde se encuentra el método que vamos a ejecutar de forma asíncrona, el nombre del propio método y un contenedor con los parámetros que necesite dicho método. Una vez lancemos la ejecución del método, veremos la gran diferencia con respecto a ejecutar el método de la forma JATMAsyncClass::waitAndInfo(10000), y esta es que, a diferencia de la primera opción, esta vez el cliente no se quedará congelado esperando a que la ejecución finalice, sino que podremos seguir navegando por el sistema y veremos el infolog cuando el proceso haya termiando.
Este método, también nos permite lanzar llamadas a un método ‘Callback’. Si ya habéis trabajado con programación asíncrona sabréis de que estoy hablando, pero si no es el caso, se trata de realizar la llamada a un método cuando la ejecución finaliza. Esto no se puede hacer de la forma tradicional, puesto que, imaginemos que tengo los métodos a(), b() y c(), que se ejecutan uno detrás de otro, y el método b() es asíncrono. En este caso, es muy probable que el método c() se ejecute antes de finalizar la ejecución del método b(). Y podéis pensar, ok, ¿y cuál es el problema?, pues el problema es que en este caso, el método c() necesita algún valor que es resultado del método b(), por lo que «tiene que esperar» a que este finalice.
También podéis pensar, ok, pues lo que hago es que, en la última línea del método b(), llamo al método c() y arreglado. Ok, pero no. El caso es el siguiente:
Desde un formulario, voy a hacer una llamada asíncrona utilizando el runAsync a un método estático de una clase, y una vez que finalice el método estático, necesito ejecutar un método del fomulario basándose en uno de los valores calculados por el método anterior. Este es el caso en el que necesitaré realizar una llamada callback, puesto que, desde la clase no puedo lanzar el método del formulario, y por otro lado, necesito que el método de la clase finalice antes de llamarlo. Y ¿cómo hacemos eso? Pues vamos a verlo. Lo primero es crear el método callback dentro del formulario, el cual recibirá por parámetros un objeto del tipo AsyncTaskResult.
1 2 3 4 5 6 7 8 9 |
public void callBackFormMethod(AsyncTaskResult _asyncTaskResult) { container ret; info('Ejecutando método callback'); ret = _asyncTaskResult.getResult(); info (conPeek(ret, 1)); } |
Y, posteriormente, llamamos al método runAsync para lanzar la llamáda al método estático de la clase e indicar que una vez finalice, debe comenzar la ejecución del método callback.
1 2 3 4 5 |
element.runAsync(classNum(JATMAsyncClass), staticMethodStr(JATMAsyncClass, waitAndInfo), [10000], System.Threading.CancellationToken::None, formMethodStr(JATMAsyncForm, callBackFormMethod)); |
Como podéis ver, la llamada es prácticamente la misma, con la diferencia de que en el último parámetro indicamos el nombre del método que debe ejecutarse cuando finalice el proceso. Con esto conseguiríamos nuestro objetivo, que era, ejecutar una operación «pesada» desde el formulario de forma asíncrona, y a su finalización, lanzar un método del formulario (externo a la ejecución asíncrona) que necesita trabajar con valores calculados por ella.
Y hasta aquí lo que quería mostraros hoy sobre la ejecución de procesos de forma asíncrona en Microsoft Dynamics 365 for Finance and Operations, aunque, también tengo que comentar, que si vamos a lanzar procesos muy muy largos, la mejor forma es la que hemos utilizado siempre en Dynamics AX, y no es otra, que la ejecución por lotes. En el próximo post hablaremos sobre este tema, y para ello veremos el funcionamiento del framework SysOperation y de las posibilidades que nos brinda.
Hasta entonces, nos vemos por las redes sociales! 🙂