-1

I'm having some trouble with a function that returns before ajax call from for loop ends. I do several ajax call which I get the result from and use them on my then chain.

Here is my code

const delay = t => new Promise(resolve => setTimeout(resolve, t));
(async () => {
    await delay(timeout);
    let results: boolean[] = [];
    for (let i = 0; i < times; i++) {
        await this.ajaxService.postWithData("/api/v1/..",
            arg,
            (data) => {
                results[i] = this.checkStep(data);
            },
            () => {
            });
    }
    return results; // This returns before for loop containing ajaxServces calls ends
})().then((results) => {
    // Do some things here using `results`
});

postWithData

public request = (options: Options, successCallback: Function, errorCallback?: Function): void => {
        var that = this;
        $.ajax({
            url: options.url,
            type: options.method,
            data: options.data,
            cache: false,
            success: function (d) {
                successCallback(d);
            },
            error: function (d) {
                if (errorCallback) {
                    errorCallback(d);
                    return;
                }
            }
        });
    }

I've checked SO answers, e.g. https://stackoverflow.com/a/40329190/3264998

Gerald Hughes
  • 5,771
  • 20
  • 73
  • 131
  • `ajaxService.postWithData()` must return a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) in order for [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) to actually pause execution until the request is complete. Right now, it doesn't have a return value. – Patrick Roberts Jan 21 '19 at 04:30

2 Answers2

1

your request wrapper is not great and unfortunately Aaron's isn't much better. I haven't used jQuery much in recent years, but I think it has Promise-based APIs now too. Either way you'll want something generic that basically gives you a "promisified" version of $.ajax so you can work with await -

const defaultOpts =
  { method: 'GET', cache: false }

const request = (opts = {}) =>
  new Promise
    ( (resolve, reject) =>
        $.ajax({ ...defaultOpts, ...opts })
         .done((req, status, res) => resolve(res))
         .fail((req, status, err) => reject(err))
    )

You haven't provided a Minimum, Complete, Verifiable Example (MVCE) so it makes it difficult help you. We don't know about times or checkStep so some guesses were made about your intentions. Something like this might help get your wheels turning again -

const main = async () =>
{ const steps =
    [ request ("/api/v1/something..")
    , request ("/api/v1/another..")
    , request ("/api/v2/andthis..")
    ]

    const res =
      await Promise.all(steps)

   console.log(res)
    // [ <result1>, <result2>, <result3> ]

    // I don't know what your checkStep does
    // but something like this might work
    res.forEach(r => this.checkStep(r))

    // otherwise
    // const checked =
    //   res.map(r => this.checkStep(r))
    // ...

    return res
}

main()
  .then(res => console.log("all results", res))
  // [ <result1>, <result2>, <result3> ]
  .catch (e => ...)

If one request is dependent on another, putting the requests in an array and then calling Promise.all is not a great fit. In the example below we query all the posts for a particular author. The fictional API we're querying asks for the author's username but we only know the author's id. In this case, we look up the author's username first, then we can query the posts by username -

const authorById = id =>
  request({ url: '/authors', data: { id } })

const postsByAuthor = username =>
  request ({ url: '/posts', data: { author: username } })

const main = () =>
{ const author =
    await authorById (10)
    // { id: 10, username: alice, email: alice@email.lol }

  // this request depends on the result of the first request
  const posts =
    await postsByAuthor (author.username)
    // [ { postId: 9, title: "hello world" }, { postId: 11, ... }, ... ]

  // ...
}
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • Thanks for the answer. It seems theres a problem when mapping. `Property 'map' does not exist on type 'Promise<{}[]>'` – Gerald Hughes Jan 21 '19 at 08:56
  • 1
    @GeraldHughes I try to include a functional code snippet in all of my answers but in the case of your question here, too much information was missing to make that possible. I fixed the `.map` mistake and updated my answer with my best guesses at your intentions. I hope it helps :D – Mulan Jan 21 '19 at 15:45
0

Something like this should work

public request = (options: Options, successCallback: Function, errorCallback?: Function): Promise => {
        return new Promise( (resolve, reject) => {
        var that = this;
        $.ajax({
            url: options.url,
            type: options.method,
            data: options.data,
            cache: false,
            success: function (d) {
                resolve(successCallback(d));

            },
            error: function (d) {
                if (errorCallback) {
                    reject(errorCallback(d));

                }
            }
        })};
    }

Also, you specified a return type for your function, would you be using TypeScript? Not sure if Promise return type is valid. But the idea is that you need to return a promise.

Aaron
  • 1,208
  • 1
  • 9
  • 21