9

I am currently trying to push to an array (attribute within a Mongo Model), from a list of items I receive through a request. From those items, I loop through them to see which one is currently in the db and if that is not the case, then I create a new Item and try to save it. I am using promises to accomplish this task but I am unable to figure out why the array is empty after all the promises have fulfilled.

var q     = require('q');

var items_to_get = ['1', '2', '3']; // example array

var trans = new Transaction({
    items   : []
});

var promises = [];

for (var i = 0; i < items_to_get.length; i++) {

  var ith = i; //save current i, kinda hacky
  var deferred = q.defer(); //init promise

  //find an existing item
  Item.findOne({simcode: items_to_get[ith]}, function(err, item) {
      trans.items.push(item); // push item to transaction
      deferred.resolve(item); // resolve the promise
  });
  promises.push(deferred); // add promise to array, can be rejected or   fulfilled
};

q.allSettled(promises).then(function(result) {
  console.log(trans.items); //is empty
  trans.save();
}

EDIT Resolved: Code bellow, based on http://jsbin.com/bufecilame/1/edit?html,js,output .. credits go to @macqm

var items_to_get = ['1', '2', '3'];
var promises     = []; //I made this global

items_to_get.forEach(item) {
  upsertItem(item);
}

q.allSettled(promises).then(function(result) {
  //loop through array of promises, add items 
  result.forEach(function(res) { 
    if (res.state === "fulfilled") {
      trans.items.push(res.value);
    }
  });
  trans.save();
  promises = []; //empty array, since it's global.
}

//moved main code inside here
function upsertItem(item) {
  var deferred = q.defer(); //init promise
  //find an existing item
  Item.findOne({simcode: item}, function(err, item) {
    deferred.resolve(item); // resolve the promise
    // don't forget to handle error cases
    // use deffered.reject(item) for those
  });
  promises.push(deferred); // add promise to array
}

2 Answers 2

6

This is how I did it without any 3rd party libraries.

Since I just needed to defer and I'm on ES2017 I figured it was best not to over complicate things with unnecessary dependencies.

'use strict';

/**
 * @param {function(*)} callee
 * @param {Array} args
 * @returns {Promise.<*>}
 */
const defer = (callee, args) => {
    return new Promise(resolve => {
        resolve(callee(...args));
    });
};

/**
 * @param {Number} one
 * @param {Number} two
 * @param {Number} timeout
 * @returns {Promise.<Number>}
 */
const asyncFunction = (one, two, timeout) => {
    return new Promise(resolve => {
        setTimeout(resolve, timeout, one + two);
    });
};

let promises = [];
promises.push(defer(asyncFunction, [3, 7, 0])); // returns immediately
promises.push(defer(asyncFunction, [10, 20, 100])); // returns after 100ms
promises.push(defer(asyncFunction, [55, 45, 50])); // returns after 50ms

Promise.all(promises).then(results => {
    console.log(results);
});

Run the above code and you'll get [ 10, 30, 100 ].

Sign up to request clarification or add additional context in comments.

Comments

1

Instead of

promises.push(deferred);

... try ...

promises.push(deferred.promise);

Also, since your promises are resolving to the item that was saved anyway, you can use the result of q.allSettled(...) as your items:

q.allSettled(promises).then(function(results) {
    trans.items = results;
    trans.save();
});

1 Comment

Seems like each result will be an object of {state: ..., value: ...} structure, where value is an item you want, so you may have to do an extra step to get them out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.