1

I am a JavaScript beginner. have an async function, that has an await method in it, problem is that after obtaining the return value from that async function when I am passing it as a parameter to another function and iterating over it using a forEach it says data.ForEach is not a function

Here is my code

async function javed(){
  var map1 = new Map();

  map1 = await createMap();


  return map1;


 }

 function createMap(){

  var map2 = new Map();
  map2.set('11', true);
  map2.set('22', true);
  map2.set('33', false);
  map2.set('44', true);

  return map2;


 }

  let x = this.javed();
  this.Dome(x);



  function Dome(res){

    res.forEach(x =>{
      console.log('inside dome', x);

    })

  }

This shows the following error

 Uncaught TypeError: res.forEach is not a function at Dome (myfile.js:83:9)

Also I am not getting during the iteration how would I get the individual key and value if I want to manipulate or just console them?

Amax1
  • 17
  • 1
  • 6
  • 2
    `after obtaining the return value from that async function` Since it's an async function, the return value is a promise. You will need to `await` the promise to get the `Map`. The `await` inside `javed` won't cause code outside of `javed` to wait. – Nicholas Tower Jul 19 '23 at 13:53

3 Answers3

1

Your problem is, that all async functions return a Promise. So you're not calling forEach() on a Map as you would expect, but instead on a Promise which does not have that function. Use let x = await javed(); in order to resolve that promise and get back a Map.

Generally I would advise you to use the this keyword sparingly in JS, you don't need it at all in your example and it can often be something other that what you expect. Also, try not to use var anymore. const and let are much more isolated from external scripts and their scoping is easier to understand.

Here's a working example with the added await. If top-level await is not supported, you can wrap the behavior in a new async function as I have done here:

async function javed() {
  var map1 = new Map();
  // createMap is not async, so no reason to await it
  map1 = await createMap();
  return map1;


}

function createMap() {
  var map2 = new Map();
  map2.set('11', true);
  map2.set('22', true);
  map2.set('33', false);
  map2.set('44', true);
  return map2;
}

async function doEverything() {
// you need to await javed
let x = await this.javed();
this.Dome(x);
}
// call the async function
doEverything();



function Dome(res) {

  res.forEach(x => {
    console.log('inside dome', x);

  })

}

EDIT: Yes, if you need a promise to resolve, you need to await it, requiring an async function. The "old" way of using Promises (which await is actually just syntactic sugar for) does not require you to make the function async, but executes non-linearly. Here's an example:

// I've cleaned up the code a bit, removing this and var

async function javed() {
  // the function is async, so it always returns a Promise. No await necessary.
  return createMap();
}

function createMap() {
  var map2 = new Map();
  map2.set('11', true);
  map2.set('22', true);
  map2.set('33', false);
  map2.set('44', true);
  return map2;
}

function Dome(res) {
  res.forEach(x => {
    console.log('inside dome', x);
  })
}


console.log("before javed");
// call javed, then Dome without a top level async function but the then method of a Promise:
// less verbose than javed().then(function(resolvedMap){Dome(resolvedMap)});
javed().then(Dome);
console.log("this will be executed before the callback function Dome!");
Taxel
  • 3,859
  • 1
  • 18
  • 40
  • (assuming createMap was async) so then that means everytime I return a value from a async function (here from javed) , the caller needs to call it with await and it needs to have the async keyword too? But that will create a chain of Async await right? Because I checked and top level await was not supported. I use chrome. So I cannot have the data in a normal synchronous flow if I need it? – Amax1 Jul 19 '23 at 14:16
  • I've updated my answer. – Taxel Jul 19 '23 at 14:32
  • I get it. but if my javed function is returning the map value and this mapvalue I need to pass it to the Dome function which I cannot afford to wrap in an async as the rest of the code executes synchronously, should it be like this : javed( ). then( x => Dome(x)) – Amax1 Jul 19 '23 at 14:39
  • Yes. Or `javed().then(Dome);` or `javed().then(function(resolvedMap){Dome(resolvedMap)})` – Taxel Jul 19 '23 at 14:41
1

The returned value of an async function is a promise so you have to resolve it with await or .then():

async function javed(){

  var map1 = new Map();

  map1 = await createMap();


  return map1;


 }

 function createMap(){

  var map2 = new Map();
  map2.set('11', true);
  map2.set('22', true);
  map2.set('33', false);
  map2.set('44', true);

  return map2;


 }

  this.javed().then(x => this.Dome(x));
  
 
  (async()=>{

    const x = await this.javed();
    this.Dome(x);
  
  })();



  function Dome(res){

    res.forEach(x =>{
      console.log('inside dome', x);

    })

  }

A third option would be using only await in a top-level of a module:

<script type="module">

async function javed(){

  var map1 = new Map();

  map1 = await createMap();


  return map1;


 }

 function createMap(){

  var map2 = new Map();
  map2.set('11', true);
  map2.set('22', true);
  map2.set('33', false);
  map2.set('44', true);

  return map2;


 }

 const x = await javed();
 Dome(x);



  function Dome(res){

    res.forEach(x =>{
      console.log('inside dome', x);

    })

  }

</script>
Alexander Nenashev
  • 8,775
  • 2
  • 6
  • 17
  • suppose the data returned from javed is to be taken in a normal method (not async) means I cannot create a chain of await and async callers how would I handle that? What Im saying is if it is like let x = this.javed( ) and then in the next line it is this.passdata(x) how would I handle that in one go? – Amax1 Jul 19 '23 at 14:29
  • @Amax1 you can't do that, because `x` will be a promise, not a value of this fulfilled promise... you have the 3rd option, i'm adding to the answer – Alexander Nenashev Jul 19 '23 at 14:33
  • @Amax1 added, it's a top-level `await` in a module – Alexander Nenashev Jul 19 '23 at 14:35
  • @Amax1 i've upvoted your question, so you can now upvote my answer (the arrow up) for my effort of providing the module version, thanks! – Alexander Nenashev Jul 19 '23 at 14:36
  • Im little unclear by what you meant when you said await at top level of module. I dont see an await keyword before or after script tags. The script tags have module in them. Moreover I am not using Script tag as Im not using plain JS and HTML, Its a template concept as I am using Salesforce LWC – Amax1 Jul 19 '23 at 16:15
  • @Amax1 so use `await` inside `async` or `.then()` – Alexander Nenashev Jul 19 '23 at 16:25
0

You are trying a forEach over a promise. The method below is an async method, but it can return the value without async/await because createMap is not returning a promise.

async function javed(){
  var map1 = new Map();

  map1 = await createMap();


  return map1;

 }

OR

You need to use .then to get the value asynchronous like below

javed()
   .then(res => {
      Dome(res);
   });

Take a look on this explanation

Aldo Benardes
  • 48
  • 1
  • 8