'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:

  1. 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 )

  1. An async function as that one in your example, is just a function that returns a Promise, 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