'Trying to retrieve data in JS that is in the function to be used outside of the function
So I've read quite a few articles on the asyn nature of JS, so I'm kind of understanding what I'm doing wrong but I still don't understand how callbacks fix the asyn nature to allow you to retrieve a result. In my code, I am using the YouTube Data API to retrieve the video of movies that I am embedding in my project. I can get the video to console login to my function but I need that videoId outside of the function. How would I use a callback to extract that data?
Here is my code:
// 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);
Solution 1:[1]
Callbacks do not resolve that behaviour, if you do:
let variable;
object.onevent = (event) => variable = event
console.log(variable) // undefined
It's the same of doing:
let variable;
promise.then(result => variable = result)
console.log(variable) //undefined
This happens because the variable declaration and the console.log
are placed in queue of the EventLoop synchronously hence executed sequentially, immediately, while both the callback mechanism and the promise mechanism are deferred.
If you want to do something relative to a global/upper-scoped variable, and you know that that variable will be changed as a consequence of an asynchronous task, you must necessarily do that inside the callback if you are using callbacks, or in the then()
callback of a promise.
Consider two more things:
- A callback based task, can easily be converted in Promise based:
object.onevent = (event) => // do something
becomes:
const promise = new Promise( resolve => object.onevent = resolve )
promise.then( event => // do something )
- An
async
function as that one in your example, is just a function that returns aPromise
, based on JavaScript generators, so inside of it it "looks" synchronous, but it all happens asynchronously:
const asynchronousFn = async () => {
const something = await somethingAsync()
doSomethingSynchronous()
return something
}
is the same of:
const asynchronousFn = () => somethingAsync().then(something => {
doSomethingSynchronous()
return something
})
The only real reason why Promises
and then async/await
were introduced in ECMA was to avoid Callbacks Hell phenomenum.
There are patterns that help you to manage this kind of scenarios, one of this is the Observable Pattern, and there are tools that help us doing that ( observables ) : https://rxjs.dev/guide/observable
But this is all another matter!
NOTE: There is an active stage4 proposal to introduce ES modules that are totally asynchronous. This means that you will be able to do something like:
let variable;
const somethingThatChangesVariableAsynchronously = async () => variable = "Something"
const somethingThatUsesVariable = () => console.log(variable)
await somethingThatChangesVariableAsynchronously()
somethingThatUsesVariable() // Something
This means that the whole module is asynchronous but you can write a sort of synchronous code inside of it as you do inside async functions. https://github.com/tc39/proposal-top-level-await
Solution 2:[2]
You have to wait your getMovieTrailer()
to return, then assign idForMovie
to the returned value. For this you will need another async
function, because await
is only available within async
functions
// 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);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | ABDULLOKH MUKHAMMADJONOV |