Las funciones recursivas son un tema con el que rara vez me encuentro.
Para darte un poco de perspectiva, las ocasiones en que he necesitado funciones recursivas se pueden contar con los dedos de una mano.
Esta es una de esas situaciones en las que una función recursiva es probablemente el mejor camino a seguir.
Muchas gracias a NicoPer quien publicó esta pregunta en el foro oficial de Microsoft Power Query.
En resumen, su escenario es este:
- Él toma sus datos de esta url –
https://api.binance.com/api/v1/ticker/allPrices
- Actualización automática –tiene su libro abierto en todo momento y su consulta se actualiza a cada instante
El problema: la conectividad
A veces, el servicio de ese sitio web no funciona o su conexión a internet falla durante una actualización, lo que hace que aparezca una ventana con el mensaje de error y también un botón para hacer clic:
Aquí está el asunto. Para que pueda «rodar la pelota» de nuevo, necesita hacer clic en el botón Ok que no resulta muy conveniente en su caso, ya que solo quiere que las cosas se ejecuten de forma automática como debería ser.
La Solución: una función recursiva de REINTENTO
En realidad, no puedo hacer VBA, por lo que no pude darle una solución basada en VBA como lo estaba solicitando. Esperaba que una Leyenda de VBA pudiera intervenir y, con suerte, me diera una solución VBA para que pudiera saltarse el mensaje de error «DataSource.Error».
Pero antes de comenzar con la solución basada en Power Query, puedes leer este artículo de Daniil Maslyuk sobre las funciones recursivas. Toma en cuenta que en este artículo él nos dice que debe usarse List.Generate para este escenario, pero el caso específico que estamos tratando en este artículo es uno de esos casos en los que una función recursiva es en realidad la mejor opción.
Así que esto es lo que terminé haciendo…
Imagina que alguien llama a la puerta cada 10 segundos hasta que alguien abre la puerta. Eso es básicamente lo que pretende hacer esta función recursiva y es por eso por lo que quiero que seas cuidadoso al respecto, ya que algunos servicios te bloquearán si realizas demasiadas llamadas en un momento dado o podrías terminar en un bucle sin fin. Power Query tiene sus propias características de seguridad para evitar bucles infinitos, pero aún así podrían ocurrir.
Lo que terminé creando fue una función que básicamente intentará consultar los datos de esa URL cada 10 segundos hasta que finalmente obtenga algunos datos de ella.
El primer paso fue crear la función que intentará obtener los datos del sitio web.
Call = ()=> Web.Contents(«https://api.binance.com/api/v1/ticker/allPrices»)
El segundo paso fue crear esa función de Retry (reintento) que terminó así:
fxRetry = (MyCall as function) =>
let
Buffered = Binary.Buffer (MyCall() ),
Output = if Record.Field(try Buffered , «HasError») = false then
Buffered else
Function.InvokeAfter(()=>@fxRetry(MyCall), #duration(0,0,0,10) )
in
Output
Ahora intentemos depurar esa función línea por línea para ver qué sucede:
- El nombre de la función es fxRetry
- Necesita una función como parámetro, aquí usaremos la función «Call».
- En el paso Buffered, usamos Binary.Buffer para almacenar en caché el resultado de la función MyCall.
- En el paso de Output, hacemos una prueba para verificar si el paso de Buffered tuvo algún error. Si no tuvo ningún error, simplemente emitiremos el resultado del paso Buffered; de lo contrario, ejecutaremos la función fxRetry de nuevo cada 10 segundos.
Ahora que tenemos las dos funciones que necesitábamos, podemos crear otra consulta que nos dará el resultado que necesitamos en nuestra hoja de cálculo de Excel y este es el código para eso:
Output = let
Source= fxRetry(Call),
ReadBinary = Json.Document( Source ),
ListOfRecordsToTable = Table.FromRecords( ReadBinary ),
ChangedType = Table.TransformColumnTypes(ListOfRecordsToTable,{{«symbol», type text}, {«price», type number}})
in
ChangedType
¡y listo! ahora tienes una solución con una función de reintento (RETRY).
Puedes descargar el libro de trabajo en el botón de abajo por si quieres verlo.