简体   繁体   中英

Why can't I move “await” to other parts of my code?

Edit2: Solution at the bottom

I am using the chrome-console and I am trying to output fetched data, and I only get the desired output by writing "await" at exactly the right place, even though another solution can do it earlier and I don't know why/how it works.

solution() is the "official" solution from a web-course I am doing. Both functions return the same, currently. In myFunction I tried writing "await" in front of every used function and make every function "async", but I still can't replace the "await" inside log, even though the other solution can.

const urls = ['https://jsonplaceholder.typicode.com/users']

const myFunction = async function() {
                                          // tried await before urls/fetch (+ make it async)
   const arrfetched = urls.map(  url => fetch(url) );   
   const [ users ] = arrfetched.map( async fetched => {  //tried await in front of arrfetched
       return (await fetched).json();                   //tried await right after return
   });
   console.log('users', await users);                   // but can't get rid of this await
}

const solution = async function() {

  const [ users ] = await Promise.all(urls.map(async function(url) {
      const response = await fetch(url);
      return response.json();
  }));
  console.log('users', users);                          // none here, so it can be done 
}

solution();
myFunction();

I would think "await" works in a way that makes:

   const a = await b;
   console.log(a);       // this doesn't work

the same as

   const a = b;
   console.log(await a); // this works

but it doesn't, and I don't understand why not. I feel like Promise.all does something unexpected, as simply writing "await" in the declaration can't do the same, only after the declaration.

Edit1: this does not work

const myFunction = async function() {

    const arrfetched = await urls.map( async url => await fetch(url) );
    const [ users ] = await arrfetched.map( async fetched => {
        return await (await fetched).json();
    });
    console.log('users', users);
}

Edit2: Thanks for the help everyone , I tried putting ".toString()" on a lot of variables and switching where I put "await" in the code and where not. As far as I understand it, if I don't use Promise.all then I need to await every time I want to use (as in the actualy data, not just use) a function or variable that has promises . It is insufficient to only have await where the data is being procensed and not further up. In the Edit1 above users runs bevore any other await is complete, therefore no matter how many awaits i write in, none are being executed. Copying this code in the (in my case chrome-)console demostrates it nicely:

const urls = [
      'https://jsonplaceholder.typicode.com/users',
    ]
    const myFunction = async function() {

      const arrfetched =  urls.map( async url => fetch(url) );
      const [ users ] =  arrfetched.map( async fetched => {
          console.log('fetched', fetched);
          console.log('fetched wait', await fetched);
          return (await fetched).json();
      });
      console.log('users', users);
      console.log('users wait', await users);
    }

    myFunction();

// Output in the order below:
// fetched()
// users()
// fetched wait()
// users wait()

TL; DR: Promise.all is important there, but it's nothing magical. It just converts an array of Promises into a Promise that resolves with an array.

Let's break down myFunction :

const arrfetched = urls.map(  url => fetch(url) );

This returns an array of Promises, all good so far.


const [ users] = arrfetched.map( async fetched => {
  return (await fetched).json();
});

You're destructuring an array to get the first member, so it's the same as this:

const arr = arrfetched.map( async fetched => {
  return (await fetched).json();
});
const users = arr[0];

Here we are transforming an array of promises into another array of promises. Notice that calling map with an async function will always result in an array of Promises.

You then move the first member of that array into users , so users now actually contains a single Promise. You then await it before printing it:

console.log('users', await users);

In contrast, the other snippet does something slightly different here:

const [ users ] = await Promise.all(urls.map(async function(url) {
  const response = await fetch(url);
  return response.json();
}));

Once again, let's separate the destructuring:

const arr = await Promise.all(urls.map(async function(url) {
  const response = await fetch(url);
  return response.json();
}));
const users = arr[0];

Promise.all transforms the array of Promises into a single Promise that results in an array. This means that, after await Promise.all , everything in arr has been awaited (you can sort of imagine await Promise.all like a loop that awaits everything in the array). This means that arr is just a normal array (not an array of Promises) and thus users is already awaited, or rather, it was never a Promise in the first place, and thus you don't need to await it.

Maybe the easiest way to explain this is to break down what each step achieves:

 const urls = ['https://jsonplaceholder.typicode.com/users'] async function myFunction() { // You can definitely use `map` to `fetch` the urls // but remember that `fetch` is a method that returns a promise // so you'll just be left with an array filled with promises that // are waiting to be resolved. const arrfetched = urls.map(url => fetch(url)); // `Promise.all` is the most convenient way to wait til everything's resolved // and it _also_ returns a promise. We can use `await` to wait for that // to complete. const responses = await Promise.all(arrfetched); // We now have an array of resolved promises, and we can, again, use `map` // to iterate over them to return JSON. `json()` _also_ returns a promise // so again you'll be left with an array of unresolved promises... const userData = responses.map(fetched => fetched.json()); //...so we wait for those too, and destructure out the first array element const [users] = await Promise.all(userData); //... et voila! console.log(users); } myFunction(); 

Await can only be used in an async function. Await is a reserved key. You can't wait for something if it isn't async. That's why it works in a console.log but not in the global scope.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM