0

I write a validate function where I want to check if a array is valid to be instert. I want to return multiple errors. I searched for that topic but couldnt get it runnig with promises, async-module. Im very new to NodeJS and would be very grateful if you could help me.

function validateNewResultUnits(sets, req) {
  var validate = [];
  sets.forEach(function(setItem) {
    setItem.forEach(function(item) {
      if (item.hasOwnProperty("name") == false) {
        validate.push({
          'name': 'Name has to be set'
        });
      } else {
        if (item.name === "" || item.name === null || item.name === undefined) {
          validate.push({
            'name': 'Name cannot be empty'
          });
        } else {
          Exercise.forge({
            name: req.body.name
          }).fetch({
            withRelated: [{
              'resultUnits': function(qb) {
                qb.where('name', item.name);
              }
            }]
          }).then(function(exercise) {
            console.log("Länge:" + exercise.related('resultUnits').length);
            if (exercise.related('resultUnits').length === 0)
              validate.push({
                'name': 'ResultUnit not found'
              });
          }).catch(function(error) {
            validate.push({
              'name': 'An Error occured'
            });
          });
        }
      }

      if (item.hasOwnProperty("value") == false) {
        validate.push({
          'value': 'Value has to be set'
        });
      } else {
        if (item.value === "" || item.value === null || item.value === undefined) {
          validate.push({
            'value': 'Name cannot be empty'
          });
        } else {
          if (isNaN(item.value)) validate.push({
            'value': 'Value has to be number'
          });
        }
      }
    });
  });
  return validate;
}

var validate = validateNewResultUnits(req.body.sets, req);
console.log(validate);
if (validate.length == 0) {
  // ...
}

The console.log(validate); return undefined before the function is ready to return something. Thanks a lot.

EDIT: Promise Attempt (inside the second forEach)

var promise = new Promise(function(resolve) {
                                Exercise.forge({name: req.body.name })
                                .fetch({
                                    withRelated: [{'resultUnits': function(qb) {
                                        qb.where('name',item.name)
                                    }}]
                                }).then(function(exercise) { 
                                console.log("Länge:"+exercise.related('resultUnits').length);
                                if (exercise.related('resultUnits').length === 0)
                                    resolve({'name':'ResultUnit not found'});
                                }).catch(function(error) { resolve({'name': 'An Error occured'}); });
                            });
                            promise.then(function(result) {
                                validate.push(result);
                            });
orgertot
  • 197
  • 16

1 Answers1

3

The problem is in Exercise.forge({name: req.body.name }).fetch(...) as it runs asynchronously which makes the validateNewResultUnits function continues its execution till it returns undefined result.

I would suggest that you change that code to embrace promises instead of those ugly async callbacks, that will require changes in the validateNewResultUnits function itself and any of its callers. Here is some resources that will help you understand promises very quickly:

https://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/

Understanding promises in node.js

http://howtonode.org/promises

https://vimeo.com/144550711

https://vimeo.com/144550811

https://vimeo.com/144550913


EDIT

Here is a copy of the provided code but using promises instead

function validateNewResultUnits(sets, req) {
  return new Promise(function(resolve, reject){
    var validate = [];
    var internalPromises = [];
    sets.forEach(function(setItem) {
      setItem.forEach(function(item) {
        if (item.hasOwnProperty("name") == false) {
          validate.push({'name': 'Name has to be set'});
        } else {
          if (item.name === "" || item.name === null || item.name === undefined) {
            validate.push({'name': 'Name cannot be empty'});
          } else {
            var internalPromise = Exercise.forge({name: req.body.name}).fetch({
                withRelated: [{
                  'resultUnits': function (qb) {
                    qb.where('name', item.name);
                  }
                }]
              });
            internalPromise.then(function(exercise) {
              console.log("Länge:" + exercise.related('resultUnits').length);
              if (exercise.related('resultUnits').length === 0)
                validate.push({'name': 'ResultUnit not found'});
            }).catch(function(error) {
              validate.push({'name': 'An Error occured'});
            });
            internalPromises.push(internalPromise);
          }
        }

        if (item.hasOwnProperty("value") == false) {
          validate.push({'value': 'Value has to be set'});
        } else {
          if (item.value === "" || item.value === null || item.value === undefined) {
            validate.push({'value': 'Name cannot be empty'});
          } else {
            if (isNaN(item.value))
              validate.push({'value': 'Value has to be number'});
          }
        }
      });
    });

    if(internalPromises && internalPromises.length) {
      Promise.all(internalPromises).then(function (value) {
        console.log(value);
        resolve(validate);
      }, function (reason) {
        console.log(reason);
        resolve(validate);
      });
    } else {
      resolve(validate);
    }
  });
}

var validatePromise = validateNewResultUnits(req.body.sets, req);
validatePromise.then(function(result){
  console.log(result);
  if (result.length == 0) {
    // ...
  }
});
Community
  • 1
  • 1
mkinawy
  • 375
  • 1
  • 10
  • So if i use bluebird to make the `Exercise.forge...` promising the function would be sync? – orgertot Dec 03 '15 at 11:40
  • It won't make it sync, but it will return a promise to the caller, and the caller can attach a `.then(function(){...})` which will be called when the promise gets resolved or rejected, something like this: ``var promise = asyncFetch(); promise.then(function success(result) {}, function error(error) {});`` – mkinawy Dec 03 '15 at 11:48
  • 1
    If you're using nodejs, you don't need bluebird for basic Promise, it's already included. – Shanoor Dec 03 '15 at 11:52
  • 1
    @ShanShan Bluebird has nice utility functions however, such as `filter`, `map`, `mapSeries` and `each`, which could be useful here in replacing the `forEach` loop – SlashmanX Dec 03 '15 at 11:54
  • Okay. But my question. If i wrap the `Exercise.forge..` into a promise. Will it work that the forEach will wait? – orgertot Dec 03 '15 at 12:15
  • I don't think so because you need to handle that promise in the caller, I think you need to try that out and let us know the results. – mkinawy Dec 03 '15 at 12:20
  • As I said before, the promise should be handled by the caller, i.e. who is calling the `validateNewResultUnits` function. So your `validateNewResultUnits` should return a promise, and the `validateNewResultUnits` caller handles that returning promise. – mkinawy Dec 03 '15 at 12:39
  • Okay got it. But how do i do this..? :/ – orgertot Dec 03 '15 at 13:48
  • I will try to do that for you in a minute using the code you have submitted so far – mkinawy Dec 03 '15 at 14:09
  • Wow that would be so awesome! – orgertot Dec 03 '15 at 14:14
  • Right now I´m in the mood to say i love you. hahaha :D – orgertot Dec 04 '15 at 10:17