1

I have an array of table names, so I need to fetch response of three tables. See the below code. Once the data is appended to the dom I need to call the successMessage method, now I am using setTimeout how can I use promise in the this scenario

   let lists = ['table1', 'table2', 'table3']

   lists.map(list => {
       $.ajax({
         url:`${rootUrl}/api/('${list}')`,
         type: 'GET',
         headers: {
             accept: 'application/json'
              },
         success: res => dataDisplay(res),
         error: err => console.log(JSON.stringify(err))
       })
    })

// displaying data
   const dataDisplay = (res) => {
    switch(res.TableName){
      case 'Table1':
       $("#tbl1 p").text(res.TableOriginalName)
       $("#tbl1 .content p").text(res.TableDescription)
       break;
      case 'Table2':
       $("#tbl2 p").text(res.TableOriginalName)
       $("#tbl2 .content p").text(res.TableDescription)
       break;
      case 'Table3':
       $("#tbl3 p").text(res.TableOriginalName)
       $("#tbl3 .content p").text(res.TableDescription)
       break;
     default:
        return
     }
 }
// successfully data appended
 const successMessage = () => alert("data appended successfully")
// calling the success method once data is appended
 setTimeout(successMessage, 3000)
ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Learner
  • 8,379
  • 7
  • 44
  • 82
  • Note: Don't use `Array#map` unless you're using its return value. To just loop through an array, use `forEach` (or [any of several other options](https://stackoverflow.com/questions/9329446/for-each-over-an-array-in-javascript)). – T.J. Crowder Mar 06 '18 at 07:32

2 Answers2

1

You'd use Promise.all to wait for all of those requests to finish before showing the message. First, build up an array of the promises:

var promises = lists.map(list => $.ajax({
    url:`${rootUrl}/api/('${list}')`,
    type: 'GET',
    headers: {
        accept: 'application/json'
         },
    success: res => dataDisplay(res),
    error: err => console.log(JSON.stringify(err))
}));

then wait for them to complete

Promise.all(promises).then(() => alert("data appended successfully"));

You can also use $.when for much the same purpose, but it's awkward to call:

$.when.apply($, promises).done(() => ...);

In comments you've said that dataDisplay loads a bunch of images and you need to delay the call to successMessage until after those images have loaded. To do that, you'll need to watch for the load event on the images. This can be a bit squirrelly because the images can load before you hook the event, so we'll want to use the image's complete flag as well. Something along these lines:

Promises.all(/*...*/).then(() => {
    // Get all images in the tables we added to
    let imgs = $("#tbl1 img, #tbl2 img, #tbl3 img");

    // Hook up a function to check for completed images when we
    // see a `load` event on any of them, and fire that proactively
    imgs.on("load", checkComplete);
    checkComplete();

    function checkComplete() {
        // Count any incomplete images; remove the handler from any
        // complete image and remove it from our `imgs` set
        let incomplete = 0;
        imgs.get().forEach(img => {
            if (img.complete || img.error) {
                $(img).off("load", checkComplete);
                imgs = imgs.not(img);
            } else {
                ++incomplete;
            }
        });
        if (incomplete == 0) {
            // They're all done!
            successMessage();
       }
    }
});

That's off the top of my head, may need some tweaking, but it should get you headed the right way.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • i need to show the message only when the dataDisplay method is completed. When i tried the solution , the message is showing first then it goes to success method of ajax call – Learner Mar 06 '18 at 08:38
  • @DILEEPTHOMAS: The above **does** call your final handler after `dataDisplay` is called ([proof](https://jsfiddle.net/w1x0Lhzc/2/)), it's just `alert` is getting in the way. That's one of the many good reasons not to use `alert`: It brings everything to a screeching halt, including pending UI updates. If you put your alert in a `setTimeout(..., 50)` that'll usually give the browser a chance to do its updates. Or better yet: Don't use `alert`. – T.J. Crowder Mar 06 '18 at 08:43
  • @TJ Crowder: Insted of alert i have put console.log for checking, when i put break points and debug first it goes to the success then to dataDisplay three times it executes after that its going to the successMessage method. But when i remove the break points and refresh the successMessage invokes earlier . it doesn't wait for the appending of data to dom – Learner Mar 06 '18 at 09:13
  • @DILEEPTHOMAS: I'm sorry, but that's simply incorrect. – T.J. Crowder Mar 06 '18 at 09:15
  • @TJ Crowder: is there any solution for that one. – Learner Mar 06 '18 at 09:20
  • @DILEEPTHOMAS: I don't know what the problem you're running into is. If you're doing the above, properly, it **isn't** that `successMessage` is being called before `dataDisplay`. It's something else. I'm sorry, I can't help you with something I can't see. – T.J. Crowder Mar 06 '18 at 09:21
  • since the dataDisplay is attaching images to the dom, so the successMessage method is calling before. I will give you an update if i can reproduce the issue – Learner Mar 06 '18 at 09:49
  • @DILEEPTHOMAS: Again: No, it isn't being called first. It's almost certainly being called before the images are downloaded and displayed; that's a completely different thing. – T.J. Crowder Mar 06 '18 at 09:50
  • @T J Crowder:so only after downloading the images and appending the entire data can i call the successMessage method – Learner Mar 06 '18 at 10:07
  • @DILEEPTHOMAS: That's a different question. :-) I've added to the answer above. Happy coding! – T.J. Crowder Mar 06 '18 at 10:16
  • 1
    @T J Crowder: that worked like a champ !! removed the setTimeout. Thanks T J for the help. Just a small correction imgs are const so assignment wont work, please update it with let. Anyways thank you :) happy coding :) – Learner Mar 06 '18 at 13:12
  • @DILEEPTHOMAS: Glad that helped! (And: Oops! Fixed, thanks for letting me know.) – T.J. Crowder Mar 06 '18 at 13:20
-1

You may try this code:

let tableNames = ['table1', 'table2', 'table3']

let promiseArr = tableNames.map((table) => {
    return new Promise((resolve, reject) => {

        $.ajax({
            url:`${rootUrl}/api/('${list}')`,
            type: 'GET',
            headers: {
                accept: 'application/json'
                 },
            success: (res) => {
                dataDisplay(res);
                resolve(table);
            },
            error: (err) => {
                console.log(JSON.stringify(err));
                reject(table);
            }
          });
    }).catch((e) => {
        // if call to any of the table's url fails
        // it will come here, var 'e' contains
        // that table name, handle here
         console.log(e + " has fails"); return e; 
        });
});

Promise.all(promiseArr).
    then((result) => {
        // this will run regardless of, if call to any table fails 
        console.log("success") 
    })
    .catch((result) => {
        console.log(result + " fails");
    });

this will asynchronously call the tables, and at the end comes to Promise.all()'s then() even if call to some table fails

Fahad
  • 346
  • 2
  • 16