简体   繁体   中英

Return in a map only when promises resolve

I am using loadash map function to do some structuring of data. The data returns a set of ids for which i need to query the db and add the result to the map and then return but the data is returned later and my function returns first. How to make the return of map wait and then return in function.

let result = _.chain(value)
        .groupBy('pubId')
        .pairs()
        .map(function(currentItem) {
          let item =  _.object(_.zip(['ID', 'targetting'], currentItem));
          propsToBeDeleted.forEach(function(prop) {
            item.targetting.forEach(d => {
              item.publishername = d.RTB.name;
              item.type = d.type;
              delete d[prop];
            });
          });
          item.targetting.map(d => {
            if (d.type == 15) {
              d.name = Object.keys(JSON.parse(d.value));
              d.value = Object.values(JSON.parse(d.value))[0];
              item.dspIds = [];
            }
            if (d.type == 16) {
              let value = JSON.parse(d.value);
              d.name = Object.keys(JSON.parse(d.value)).filter(d => d != 'profileId');
              d.value = value;
              item.dspIds = d.value.profileId;
              dspAccount.find({where: {id: 139}}, (err, data) => {
                // async call wait here to get data and attach to item 
                 item.x = data ;
              });
              delete d.value.profileId;
              d.value = JSON.stringify(d.value);
            }
          });
          return item;
        })
        .value();

I also tried using promises

promises.push(new Promise((resolve, reject) => {
        if (data.id) {
          XX.upsertWithWhere({id: data.id}, payload, (err, data) => {
            if (err) {
              return reject(err);
            }
            return resolve(data);
          });
        }
      }));
    });
    Promise.all(promises).then((data) => {
      callback(null, data);
    }, err => {
      callback(err);
    });

Update i have already listed the promise .all method that doesnot work . So it cannot be called duplicate

Things are slightly tricky because you have an outer .map() and an inner .map() , containing an asynchronous element.

The asynchronous element effectively makes the whole thing asynchronous, requiring that :

  • the array of Promises generated by the inner .map() is aggregated with Promise.all(), and the aggregate Promise returned to the outer .map() .
  • the array of Promises generated by the outer .map() is aggregated with Promise.all().
  • the results (an array of items) is available only via a chained .then() .

First, to make the main code block simpler and easier to read, create a promisified version of dspAccount.findAsync() .

dspAccount.findAsync = function(query) {
    return new Promise(function(resolve, reject) {
        dspAccount.find(query, (err, data) => {
            if(err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}

Then the main code block can be written as follows :

let outerPromises = _.chain(value)
.groupBy('pubId')
.pairs()
.map(function(currentItem) {
    let item = _.object(_.zip(['ID', 'targetting'], currentItem));
    item.targetting.forEach(d => {
        item.publishername = d.RTB.name;
        item.type = d.type;
        propsToBeDeleted.forEach(function(prop) {
            delete d[prop];
        });
    });
    let innerPromises = item.targetting.map(d => {
        let value = JSON.parse(d.value);
        if (d.type == 15) { // synchronous - `innerPromises` receives a null
            item.dspIds = []; // 
            d.name = Object.keys(value);
            d.value = Object.values(value)[0];
            return null;
        }
        if (d.type == 16) { // asynchronous - `innerPromises` receives a Promise
            return dspAccount.findAsync({where: {id: 139}})
            .then(data => {
                d.name = Object.keys(value).filter(d => d != 'profileId');
                item.dspIds = value.profileId;
                delete value.profileId;
                item.x = data;
                d.value = JSON.stringify(value);
            });
        }
    });
    return Promise.all(innerPromises).then(() => item); // aggregate inner promises and return
})
.value(); //  returns the outerPromises array

Promise.all(outerPromises)  // aggregate outer promises
.then(items => {
    // do stuff with items here
})
.catch(error => {
    // something went wrong
    // log/display error 
    // take remedial action as required
});

I expect there's still some work to do. It doesn't look right that item should be mutated in the item.targetting.map() loop. The last element of item.targetting to deliver its result (synch or asynch) will determine the composition of item . That may not be what you want. If it is what you want, then more control will need to be exercised to ensure that the last result delivered is the result arising from the final element of item.targetting .

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