-1

I have a nodejs express server and I'm working with bluebird Promises for synchronize all the async stuff.

Everything works fine on localhost using an AWS RDS MySQL database, but when I uploaded the server to my AWS EC2 instance I have found a problem with this function:

var Promise = require('bluebird');
var db      = require('./db');

exports.matchValue = function (params) {
    return new Promise(function(resolve, reject) {
        var findValue = new Promise(); 
        if (params.find.includes(".")) {
            var aux = params.find.split(".");
            var matchBy = {};
            if (aux[0]) matchBy.a = aux[0];
            if (aux[1]) matchBy.b = aux[1];
            findValue = db.getValues1(params.limit,params.page,matchBy);
        }
        else {
            findValue = db.getValues2(params.limit,params.page,params.find);
        }
        findValue
        .then(function(result) {
            resolve(result);
        })
        .catch(function(err) {
            reject(err);
        });
    }); 
}

I have declared the variable findValue as a new Promise because depending of the if condition, it will receive the value of a different database query function (this functions return a Promise).

When I call this function, this is the result: "undefined is not a function".

I understand that this behaviour happens because it executes first findValue.then() than the if/else block code, and as the variable is undefined it can be a function.

I thought that declaring a variable as a new Promise it will wait until the return of the function assigned to this variable finishes, but actually is not happening.

What am I doing wrong? Can someone help me?

Thank you in advice!!

javing
  • 3
  • 2
  • **Where**, exactly, do you get the error? – T.J. Crowder Jul 23 '16 at 14:59
  • What is `params.find`? Because you're using `params.find.includes` which, if `params.find` is an array, is relatively new and won't exist on older JavaScript engines. – T.J. Crowder Jul 23 '16 at 15:00
  • I get the error on `.then(function(result)` and it only happens when the node is running on a EC2 instance, when it runs on localhost, result contains the query response, and params.find is just a string. thank you. – javing Jul 23 '16 at 15:06
  • That's very strange. It suggests that what you're getting back from `getValues1` (or `getValues2`) isn't a promise on EC2. – T.J. Crowder Jul 23 '16 at 15:12
  • It is quite strange, because both `getValues1, getValues2` are database queries (findAll), handled by Sequelize library, and in its documentation _findAll([options]) -> Promise.>_ – javing Jul 23 '16 at 15:17
  • 1
    Are you sure that all the libraries you need are properly installed on your EC2 instance? – jfriend00 Jul 23 '16 at 17:45
  • Avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi Jul 23 '16 at 18:33
  • "*I thought that declaring a variable as a new Promise it will wait until the return of the function assigned to this variable finishes*" - no, that's absolutely not how promises work. They don't convert variables into setters, and there is nothing special about the return of the function. That function you're calling would have to return a promise. [There is no magic involved](http://stackoverflow.com/a/22562045/1048572). – Bergi Jul 23 '16 at 18:36

4 Answers4

0

I have declared the variable findValue as a new Promise because depending of the if condition, it will receive the value of a different database query function (this functions return a Promise).

In JavaScript, you don't declare variables as being of any type. This line:

var findValue = new Promise();

declares a variable (var findValue) and attempts to create a promise (= new Promise()). The Promise constructor requires that you pass it a function. It tries to use its first argument as a function, and since you haven't passed it any arguments, it gets undefined for the first argument — thus the error.

You could just change it to

var findValue;

...but your code is falling prey to the unnecessary promise creation fallacy: Just use the promise you already have:

exports.matchValue = function (params) {
    var findValue;
    if (params.find.includes(".")) {
        var aux = params.find.split(".");
        var matchBy = {};
        if (aux[0]) matchBy.a = aux[0];
        if (aux[1]) matchBy.b = aux[1];
        findValue = db.getValues1(params.limit,params.page,matchBy);
    }
    else {
        findValue = db.getValues2(params.limit,params.page,params.find);
    }
    return findValue;
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks for your answer T.J. Crowder, but that was the first thing that I tested and doesn't works also! It returns the same message – javing Jul 23 '16 at 14:57
  • @javing: Then you'll need to clarify the question. Specifically, what exact line do you get the error from? – T.J. Crowder Jul 23 '16 at 14:58
  • It looks like his `db` is not promisified, and `findValue` simply becomes `undefined`. – Bergi Jul 23 '16 at 18:37
  • @Bergi: If `findValue` were `undefined`, I'd expect a different error (`Cannot read property 'then' of undefined`). – T.J. Crowder Jul 24 '16 at 08:00
  • Hi @Bergi and T.J. Crowder, I know this is very old but I found out what really happened here, I thought you two might be interested in reading my answer. I wonder if any of you know why Bluebird gave this crazy error message. Thank you! – Pedro A Oct 25 '20 at 16:04
0

Assuming your db.getValues1/db.getValues2 functions return promises (they do not appear to take a callback, and I refuse to assume you're using a synchronous DB driver in production), try the following version of your posted code:

var Promise = require('bluebird');
var db      = require('./db');

exports.matchValue = function (params) {
    var findValue;

    if (params.find.includes(".")) {
        var aux = params.find.split(".");
        var matchBy = {};
        if (aux[0]) matchBy.a = aux[0];
        if (aux[1]) matchBy.b = aux[1];
        findValue = db.getValues1(params.limit,params.page,matchBy);
    }
    else {
        findValue = db.getValues2(params.limit,params.page,params.find);
    }

    return findValue;
}

If it works, great. Here are some things to consider:

  1. You are creating a new Promise. Don't do that. Promisify standard, Node-compliant functions/libraries using Bluebird's promisification routines.

  2. Bluebird 2.x and 3.x will throw on var findValue = new Promise(). As has been pointed out, the constructor requires a function as its argument. I suspect you paraphrased the error message you're actually getting. If so, don't do that, it makes it very hard for others to figure out what's wrong.

  3. Next time you post to SO, please reduce your sample code to something anyone can run and readily repro your problem. You might find in the course of getting to the smallest repro that you solve your own problem. If you don't, it will enable folks here to more effectively help you.

0

I faced a similar issue, and coincidentally I was also using Sequelize.

Here is a one-liner to reproduce the problem:

Promise.resolve({ a: 1 }).then(([x]) => {});

Output:

Unhandled rejection TypeError: undefined is not a function

The reason for this error is that JavaScript is trying to execute [Symbol.iterator]() on the resolved value, but since it is an object it does not have a [Symbol.iterator] - the undefined in the error message is value[Symbol.iterator].

A better error message would be something in the lines of value is not iterable.

As for why this happened with you, that must be a bug with Sequelize. Please file an issue if you're still experiencing it.

Pedro A
  • 3,989
  • 3
  • 32
  • 56
  • It's not bluebird giving that error, it's the js engine. Values are attempted to be iterated by calling their `[Symbol.iterator]()` method, and if that's `undefined` the default error you get is that you're calling something that doesn't exist. Looks like your node.js version doesn't do the nicer message in parameter destructuring. – Bergi Oct 25 '20 at 16:50
  • @Bergi Ooops you are right. I will edit the answer. – Pedro A Oct 25 '20 at 18:34
  • This doesn't have anything to do with the question. Yes, you had a similar error message, but there are lots of ways to get "`undefined` is not a function." This is a different one from the one in the question, which is (as I pointed out in my answer) because of `new Promise()` (e.g., with no arguments). Yours is that you're trying to use iterable destructuring on a non-iterable object. `Promise.resolve({a: 1}).then(([x]) => {});` tries to apply iterable destructuring `[x]` to `{a: 1}`, as though you'd done this: `const [x] = {a: 1};`. It has nothing to do with promises. – T.J. Crowder Oct 26 '20 at 07:31
-1

Your .matchValue() function should simplify to :

exports.matchValue = function(params) {
    var aux = params.find.split('.');
    return (aux.length < 2) ? db.getValues2(params.limit, params.page, params.find) : db.getValues1(params.limit, params.page, { 'a': aux[0], 'b': aux[1] });
};

With this, .then(function(result) certainly won't throw because that line has disappeared, but the function's caller might well throw instead. If so, then you must (again) suspect that either db.getValues1() or db.getValues2() doesn't return a promise on the EC2 server; try logging the returned value/object to see what it is.

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
  • I have removed the if/else, and doing something similar to your answer (returning the result of the db functions to findValue and using findValue.then(result)) it works fine, thanks! – javing Jul 25 '16 at 13:28
  • Cool, though it's a mystery what the problem was and exactly what fixed it. – Roamer-1888 Jul 25 '16 at 18:17