Search code examples
node.jssoapasync-awaitpromisees6-promise

Node Js loop in Promise All not waiting to get request from SOAP


Hi I try to use for for loop data and send request to soap api. The problem is my program seems like it didn't wait to get request from soap request

here is my example data

 data = [
      {
        store: "store1",
        product: "product2",
      },
      {
        store: "store2",
        product: "product3",
      }
    ]
exports.thisIsFunc = async (req, res, next) => {
   Promise.all(data).then(result => {
      for (let item of result) {
        i++
        console.log(i)
        args.CustomerCode = item.store
        args.ItemNo = item.product
        const getApi = apiSend()
      }
    });
 }



export function apiSend() {
  return new Promise ((resolve, reject) => {
    soap.createClient(url, (err, client) => {
      client.getDataFromAPI(args, (err, result) => {
        console.log(result)
        return result
      })
    });
  });
}

as you see I try to use new Promise in sendApi function but sometimes It stop the error show up TypeError: Cannot read property 'getDataFromAPI' of undefined

sometimes it return response from api. The reason that I didn't use async,await in soap because I try to change soap function into async function but it didn't work.


Solution

  • apiSend() has the following issues:

    1. You ignore both callback errors.
    2. You never resolve() or reject() the promise you return.

    You can fix it like this:

    export function apiSend() {
      return new Promise ((resolve, reject) => {
        soap.createClient(url, (err, client) => {
          if (err) return reject(err);
          client.getDataFromAPI(args, (err, result) => {
            if (err) return reject(err);
            console.log(result)
            resolve(result);
          })
        });
      });
    }
    

    Then, in thisIsFunc() you have a number of issues:

    1. There's no point in using Promise.all() on a static array of values. It only does something useful if you pass it an array with at least one promise in it.
    2. There's no declaration of i or args
    3. You don't do anything with the promise returns from apiSend()

    It's not really clear what you're trying to do here, but if you want thisIsFunc() to return a promise that resolves with an array of results from all the calls to apiSend(), that could be structured like this:

    exports.thisIsFunc = () => {
       return Promise.all(data.map(item => {
           let args = {
               CustomerCode: item.store, 
               ItemNo: item.product
           };
           return apiSend(args);
       }));
     }
    

    This implementation uses data.map() to iterate the array and create an array of promise from calling apiSend(). It then uses Promise.all() to collect all the results from the array of promises into an array of results that is the resolved value of the single returned promise.

    It appears you were attempting to declare thisIsFunc() as an Express request handler. You can do that, but then you will need to complete the request inside that function by sending a response for both success and error conditions. As I've shown it above, this is a helper function that retrieves a set of data and then returns a promise that resolves to an array of results. You can use that in a request handler as needed or you can add more code to this to make it into a request handler that sends a response to the incoming request and returns errors responses appropriately.