Замыкания по переменным типа объекта

Немного изменив более ранний question, мой конкретный пример использования ниже, где я передаю объект, какое лучшее решение в этом случае?

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);

Он не работает, когда я использую ссылки на основе объектов, какое решение в этом случае?

Ответ 1

Ваша проблема в том, что код isync. Поэтому, что происходит, это примерно так:

  • Вы устанавливаете body.entity.type = "coke"

  • Вы делаете асинхронный вызов

  • Вы устанавливаете body.entity.type = "pepsi"

  • Вы выполняете другой асинхронный вызов

  • Интерпретатор простаивает (больше не запускается javascript)  поэтому он может выполнять сетевой код.

  • Javascript выполняет первый сетевой запрос с body.entity.type = "pepsi"

  • Javascript выполняет второй сетевой запрос с body.entity.type = "pepsi"

  • Сетевые запросы завершены (может быть в порядке, может быть, нет, в зависимости по которому поступает первый) и вызывает обратный вызов.

Таким образом, оба console.log будут иметь "pepsi", потому что объекты являются ссылками, и вы передаете тот же объект для обоих запросов.

Это IS, чтобы сделать эту работу с простыми объектами:

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

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

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

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

Если вы хотите сделать код более многоразовым, используйте конструктор:

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

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

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

Или используйте функцию, которая возвращает литералы объекта:

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

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

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

Разница между глобальной ссылкой на один объект и объект, созданный внутри функции, состоит в том, что первый является всего лишь одним объектом, разделяемым между двумя событиями, ожидающими своего существования в будущем, а затем два отдельных объекта, переданные двум отдельным событиям.

Ответ 2

Поскольку вы используете promises и асинхронные операции, обе ваши операции async выполняются "в полете одновременно.

И, в Javascript объекты передаются указателем (а не копией). Таким образом, если вы передаете объект в операцию async и что операция async будет использовать/модифицировать этот объект, а затем вы передаете один и тот же объект в другую операцию async, которая также будет использовать его, вы будете иметь две операции async, пытающиеся используйте тот же объект.

Таким образом, у вас будут непредсказуемые результаты, основанные на индивидуальном времени двух асинхронных операций. Независимо от того, какая операция асинхронного завершения завершена, последняя будет делать последнюю модификацию объекта, перезаписав ту, которая была закончена до нее.

Обычное решение здесь состоит в том, чтобы НЕ передать один и тот же объект в операции async или изменить ваши операции async, чтобы они сделали свою собственную копию переданного объекта, а затем изменили копию и вернули ее. Пока вы передаете разные объекты для каждой операции async или изменяете операции async, чтобы они не изменяли переданный объект, вы избегаете этого конфликта.

Повторяем два возможных решения:

  • Не пропускайте один и тот же объект в каждую операцию async. Создайте новый объект для каждого асинхронного вызова.
  • Измените операции async, чтобы они не изменяли передаваемый объект (они могут создавать и возвращать свой собственный объект).