Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js Promises: Push to array asynchronously & save

Tags:

arrays

node.js

q

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
}
like image 374
Claudiu S Avatar asked Mar 21 '15 17:03

Claudiu S


People also ask

Are promises executed asynchronously?

A promise is used to handle the asynchronous result of an operation. JavaScript is designed to not wait for an asynchronous block of code to completely execute before other synchronous parts of the code can run. With Promises, we can defer the execution of a code block until an async request is completed.

Are JS promises synchronous or asynchronous?

In JavaScript, promises are special objects that help you perform asynchronous operations. You can create a promise using the Promise constructor. You need to pass an executor function to it. In the executor function, you define what you want to do when a promise returns successfully or when it throws an error.

Does promise all run synchronous?

all() Method: Fulfillment: The returned promise is fulfilled, If the passed iterable is empty, then this method returns an promise synchronously which is already resolved. If all of the passed promises are fulfill, the returned Promises are fulfilled asynchronously.

Is async await better than promises?

Async/Await is used to work with promises in asynchronous functions. It is basically syntactic sugar for promises. It is just a wrapper to restyle code and make promises easier to read and use. It makes asynchronous code look more like synchronous/procedural code, which is easier to understand.


1 Answers

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 ].

like image 127
Francesco Casula Avatar answered Sep 26 '22 00:09

Francesco Casula