0

Slightly modifying an earlier question, my exact use case is below where I'm passing an object, what's the best solution in this case?

Product.prototype.list = function(body) {
    body.options = {
        hostname: endPoints.product,
        path: '/applications/' + body.entity.type,
        method: 'GET'
    };
    return remote.request(body)
        .then(function(result){
            body[body.entity.type] = result;
            return body;
        });
};

var body = {
    entity: {
        type: null
    }
};

body.entity.type = "coke";
product.list(body)
    .then(console.log); //will this have {coke: []} or {pepsi: []}

body.entity.type = "pepsi";
product.list(body)
    .then(console.log);

It's not working when I'm using object based references, what's the solution in this case?

Community
  • 1
  • 1
user2727195
  • 7,122
  • 17
  • 70
  • 118

2 Answers2

0

Your problem is that the code is async. Therefore, what's happening is something like this:

  1. You set body.entity.type = "coke"

  2. You make an async call

  3. You set body.entity.type = "pepsi"

  4. You make another async call

  5. The interpreter is idle (no more javascript to run) therefore it can execute networking code.

  6. Javascript makes first network request with body.entity.type = "pepsi"

  7. Javascript makes second network request with body.entity.type = "pepsi"

  8. Network requests complete (may be in order may be not, depending on which arrives first) and calls the callback.

So both console.log will have "pepsi" because objects are references and you're passing the same object to both requests.

It IS possible to make this work with plain objects:

var body1 = {
    entity: {
        type: "coke"
    }
};

var body2 = {
    entity: {
        type: "pepsi"
    }
};

product.list(body1)
    .then(console.log);

product.list(body2)
    .then(console.log);

If you want to make the code more reusable, either use a constructor:

function Body (type) {
    this.entity = {type: type};
}

product.list(new Body('pepsi'))
    .then(console.log);

product.list(new Body('coke'))
    .then(console.log);

Or use a function that returns object literals:

function makeBody (type) {
    return {entity: {type: type}};
}

product.list(makeBody('pepsi'))
    .then(console.log);

product.list(makeBody('coke'))
    .then(console.log);

The difference between a global reference to a single object and an object created inside a function is that the former is just one object shared between two events waiting to happen in the future, the later are two separate objects passed to two separate events.

slebetman
  • 109,858
  • 19
  • 140
  • 171
  • thanks for the enlightenment, I did this within my list function, any two cents? `body = JSON.parse(JSON.stringify(body));` to delink to referred object and create a new copy, please add this to your answer, and I'd accept, basically your `body1` and `body2` enlightened me – user2727195 Jul 04 '16 at 04:34
  • JSON.stringify and parse is expensive. You're better off writing a function to create new object literals each time. It's workable for a quick fix but I'd refactor it later on. – slebetman Jul 04 '16 at 04:48
0

Because you're using promises and asynchronous operations, both of your async operations are "in flight" at the same time.

And, in Javascript objects are passed by pointer (not by copy). So, if you pass an object into an async operation and that async operation will use/modify that object and then you pass the same object into another async operation that is going to be using it also, you will have two async operations both trying to use the same object.

As such, you will have unpredictable results based on the individual timing of the two async operations. Whichever async operation completes last will make the last modification to the object, overwriting the one that finished before it.

The usual solution here is to NOT pass the same object into both async operations or to change your async operations so they make their own copy of the object that is passed in and then modify the copy and return that. As long as you pass different objects to each async operation or change the async operations so they don't modify the passed in object, then you will avoid this conflict.

To recap, two possible solutions:

  1. Don't pass the same object into each async operation. Create a new object for each async call.
  2. Change your async operations so they don't modify the object that is passed in (they can create and return their own object).
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • thanks, I've used `body = JSON.parse(JSON.stringify(body))` within the function `list` body, is it expensive? – user2727195 Jul 04 '16 at 04:58
  • @user2727195 - Here is a discussion of [What is the most efficient way to clone an object?](http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-an-object). – jfriend00 Jul 04 '16 at 05:02