15

I would like to save a registrationId which is generated randomly in Parse cloud code, so I need to check if that value is already in the DB, I have to do this in a recursive way until I get the right string. Here is what I tried so far, the problem is that findRegistrationId() is not a promise so I cannot use then() is there any way to make it a promise or any other workaround? for cloudcode

function getRandomString()

{
    var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
    var string_length = 4;
    var randomstring = '';

    for (var i=0; i<string_length; i++) {
        var rnum = Math.floor(Math.random() * chars.length);
        randomstring += chars.substring(rnum,rnum + 1);
    }
    return randomstring;
}
function findRegistrationId()
{
    console.log("Enter findRegistrationId")
    var randomString = getRandomString();
    var query = new Parse.Query("Book");

    query.equalTo("registrationId", randomString);

    query.find.then(function(results){
        if(results.length === 0)
        {
            console.log("no registrationId duplicated")
            return randomString;
        }
        //if there is other registrationId we concatenate
        else
        {
            console.log("registrationId duplicated let's recursive it")
            return findRegistrationId();
        }
    },function(error){
        return error;
    })

}

// Use Parse.Cloud.define to define as many cloud functions as you want.
// Gets the unique cool BC identificator. The real glue of BC!
Parse.Cloud.define("GetBookId", function(request, response) {

    var promise = findRegistrationId();

    promise.then(function(result){

        console.log("success promise!!")
        response.success(result);

    }, function(error){
        console.error("Promise Error: " + error.message);

        response.error(error);

    });


});
Javier Hertfelder
  • 2,432
  • 4
  • 22
  • 36
  • Is it essential it is a synchronous function call? Can't you provide the string in a callback and make it asynchronous? Also, this is still not going to guarantee uniqueness. You don't create any kind of lock on the string you get back, so someone could be allocates the same ID. – Craig Aug 14 '13 at 10:21
  • In regards to the problem you're trying to solve: you want unique registration IDs, consider an atomic operation like increment(), when this returns it is guaranteed to be unique. e.g. if you have a record with a nextId property you can call record.increment('nextId') and in the save callback check what value you got, no-one else could get that ID. If you really have to use alpha/numeric you could create a deterministic function to convert a number to a reliable sequence. – Timothy Walters Aug 15 '13 at 01:30
  • Thanks Timothy, I think you are right but I will give the solution to @arghbleargh because he has taught me how to make a promise in Parse cloud code – Javier Hertfelder Aug 17 '13 at 10:36

2 Answers2

48

You can write your function like this:

function findRegistrationId()
{
    console.log("Enter findRegistrationId")
    var randomString = getRandomString();
    var query = new Parse.Query("Book").equalTo("registrationId", randomString);

    var promise = new Parse.Promise();

    query.find().then(function(results) {
        if (results.length == 0)
        {
            promise.resolve(randomString);
        }
        else
        {
            findRegistrationId().then(function(result) {
                promise.resolve(result);
            }, function(error) {
                promise.reject(error);
            });
        }
    }, function(error) {
        promise.reject(error);
    });

    return promise;
}
arghbleargh
  • 3,090
  • 21
  • 13
  • 8
    This answer taught me promises. +1 – Greg M. Krsak Oct 28 '13 at 07:30
  • 15
    note that this answer uses the "delayed promise return" anti-pattern; the right way to do it is "return query.find()..." and where you say promise.resolve/reject now, instead say "return Parse.Promise.as/error" – Ben Wheeler Sep 29 '14 at 17:12
  • 1
    @BenjaminWheeler, could you post an alternate version of the function that incorporates all the changes you suggest? – Kaitain Oct 04 '15 at 16:06
  • I have a function similar to this, but when I chain it, I get "cannot call method 'then' of undefined at..." It works when I just return myFunction(); inside of a chain link, but I can't do return myFunction().then(); Any idea why? – Jake T. Oct 22 '15 at 00:45
  • @GregKrsak I hope it did not. Avoid the [deferred antipatttern](http://stackoverflow.com/q/23803743/1048572)! – Bergi Oct 24 '16 at 21:21
18

@arghbleargh posted a great solution, and I'm just posting a version that uses direct returning of promises rather than resolving/rejecting, which is an anti-pattern (see https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns eg):

function findRegistrationId()
{
    console.log("Enter findRegistrationId")
    var randomString = getRandomString();
    var query = new Parse.Query("Book").equalTo("registrationId", randomString);    

    return query.find().then(function(results) {
        if (results.length == 0)
        {
            return randomString;
            // or, you could return Parse.Promise.as(randomString);
            // but the explicit promise isn't required
        }
        else
        {
            findRegistrationId().then(function(result) {
                return result;
                // or, you could return Parse.Promise.as(result);
                // but the explicit promise isn't required
            }, function(error) {
                return Parse.Promise.error(error);
            });
        }
    }, function(error) {
        return Parse.Promise.error(error);
    });    
}
Ben Wheeler
  • 6,788
  • 2
  • 45
  • 55