Así que he leído bastantes artículos sobre la naturaleza asíncrona de JS, por lo que entiendo lo que estoy haciendo mal, pero todavía no entiendo cómo las devoluciones de llamada corrigen la naturaleza asíncrona para permitirle recuperar un resultado. En mi código, uso la API de datos de YouTube para recuperar el video de las películas que incrusto en mi proyecto. Puedo obtener el video para iniciar sesión en la consola en mi función, pero necesito ese videoId fuera de la función. ¿Cómo usaría una devolución de llamada para extraer esos datos?
Aquí está mi código:
// Blank Variable to store videoId var idForMovie = ""; async function getMovieTrailer() { let resultAll = []; for (var k = 0; k < 1; k++) { let searchResults = await fetch("https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=" + encodeURIComponent(`${movieArray[k].Title} Trailer`) + "&key=" + apiKey); let search = await searchResults.json(); resultAll.push(search); } for (var i = 0; i < resultAll.length; i++) { var {items} = resultAll[i]; //object destructuring console.log(items); for (var j = 0; i < items.length; i++) { var {videoId} = items[i].id; //object destructuring console.log(videoId); idForMovie = videoId; } } //console.log(searchItems); return idForMovie; } getMovieTrailer(); console.log(idForMovie);Debe esperar a que regrese su getMovieTrailer() , luego asigne idForMovie al valor devuelto. Para esto, necesitará otra función async , porque await solo está disponible dentro de las funciones async
// Blank Variable to store videoId var idForMovie = ""; async function getMovieTrailer() { let resultAll = []; for (var k = 0; k < 1; k++) { let searchResults = await fetch("https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=" + encodeURIComponent(`${movieArray[k].Title} Trailer`) + "&key=" + apiKey); let search = await searchResults.json(); resultAll.push(search); } for (var i = 0; i < resultAll.length; i++) { var {items} = resultAll[i]; //object destructuring console.log(items); for (var j = 0; i < items.length; i++) { var {videoId} = items[i].id; //object destructuring console.log(videoId); idForMovie = videoId; } } //console.log(searchItems); return idForMovie; } async function nextFunction() { idForMovie = await getMovieTrailer(); console.log(idForMovie); }Las devoluciones de llamada no resuelven ese comportamiento, si lo hace:
let variable; object.onevent = (event) => variable = event console.log(variable) // undefinedEs lo mismo de hacer:
let variable; promise.then(result => variable = result) console.log(variable) //undefined Esto sucede porque la declaración de la variable y el archivo console.log se colocan en la cola de EventLoop de forma síncrona, por lo que se ejecutan de forma secuencial e inmediata, mientras que tanto el mecanismo de devolución de llamada como el mecanismo de promesa se difieren.
Si desea hacer algo relativo a una variable global/de ámbito superior, y sabe que esa variable cambiará como consecuencia de una tarea asíncrona, necesariamente debe hacerlo dentro de la devolución de llamada si está utilizando devoluciones de llamada, o en el then() devolución de llamada de una promesa.
Considere dos cosas más:
object.onevent = (event) => // do somethingse convierte en:
const promise = new Promise( resolve => object.onevent = resolve ) promise.then( event => // do something )async como la de su ejemplo, es solo una función que devuelve una Promise , basada en generadores de JavaScript, por lo que dentro de ella "parece" síncrona, pero todo sucede de forma asíncrona: const asynchronousFn = async () => { const something = await somethingAsync() doSomethingSynchronous() return something }es el mismo de:
const asynchronousFn = () => somethingAsync().then(something => { doSomethingSynchronous() return something }) La única razón real por la que se introdujeron Promises y luego async/await en ECMA fue para evitar el fenómeno Callbacks Hell .
Hay patrones que te ayudan a manejar este tipo de escenarios, uno de estos es el Patrón Observable, y hay herramientas que nos ayudan a hacer eso (observables) : https://rxjs.dev/guide/observable
¡Pero todo esto es otro asunto!
NOTA: Hay una propuesta activa de etapa 4 para introducir módulos ES que son totalmente asincrónicos. Esto significa que podrá hacer algo como:
let variable; const somethingThatChangesVariableAsynchronously = async () => variable = "Something" const somethingThatUsesVariable = () => console.log(variable) await somethingThatChangesVariableAsynchronously() somethingThatUsesVariable() // SomethingEsto significa que todo el módulo es asíncrono, pero puede escribir una especie de código síncrono dentro de él como lo hace dentro de las funciones asíncronas. https://github.com/tc39/proposal-top-level-await