BreezeJS - Цепочка запросов

Скажем, у нас есть объект Customer с коллекцией «Foo». Я бы хотел, чтобы моя функция "getCustomer" добавляла все Foo, которых у нее еще нет, а затем возвращала себя в качестве обещания ...

Итак, я бы хотел пообещать: получить клиента, а затем добавить все отсутствующие Foo к этому клиенту, чтобы, когда обещание будет выполнено, у клиента будут все отсутствующие foo.

Пример:

// dataservice.js

// returns Q.Promise<breeze.QueryResult>
function getCustomer(custId) {
    var query = breeze.EntityQuery().from("Customers").where("CustomerId", "==", custId);
    return this.em.executeQuery(query);
}

// returns Q.Promise<breeze.QueryResult>
function getFoosNotOnCustomer(customer) {
    var query = breeze.EntityQuery().from("Foo").where("CustomerId", "!=", customer.Id());
    return this.em.executeQuery(query);
}

Я борюсь с тем, как правильно «связать» их вместе, что делать, если ни один покупатель не найден и т. Д. Как я могу изменить «getCustomer» для этого? Я в основном пытаюсь синхронно работать с пользователем. Вот моя попытка, но она в спешке превращается в уродливый вложенный код.

   // want to return Q.Promise<Customer> that has Foos loaded
   // I think this is actually returning something like Q.Promise<Q.Promise<Customer>>
    function getCustomer(custId) {
        var query = breeze.EntityQuery().from("Customers")
                .where("CustomerId", "==", custId);

        return this.em.executeQuery(query) // return here?
               .then(function(data) {
                    // what about conditionals?
                    if(data.results.length == 1) {
                        getFoosNotOnCustomer(data.results[0]).
                        then(function (foosArray) {
                             $.each(foosArray, function(i,el) {
                                 // push foos onto customer instance
                             }
                             return custWithFoos; // return here?
                        }
                       // return something? 
                    }
                 }
    }

Вот что я в итоге сделал:

 function getCustomer(custId) {

     var query = breeze.EntityQuery().from("Customers").where("CustomerId", "==", custId);

     return manager.executeQuery(query) // return here?
         .then(addFoos)
         .then(doSomethingElse);
}
 function addFoos(data) {
     var myDefer = Q.Defer();

     if (data && data.result.length == 1) {
         var customer = data.results[0];
         var query = // get FOOS Customer doesn't have;
         manager.executeQuery(query).then(function (fooData) {
             $.each(fooData.results function (i, el) {
                 customer.Foos.push(el);
             });
             myDefer.reslove(customer);
         });
     } else {
         myDefer.resolve(undefined);
     }

     return myDefer.promise;
 }

 function doSomethingElse(customer) {
     var myDefer = Q.Defer();
     customer.SomePropert("test");
     return myDefer.resovlve(customer);
 }

 // ----- MVVM
 var custPromise = getCustomer(1).then(function (customer) {
     // do something
 });

person user210757    schedule 10.12.2013    source источник
comment
Я не понимаю, что вы пытаетесь сделать. Я могу помочь вам связать запросы ... чтобы второй запрос зависел от первого ... если вы этого хотите. Но ваш пример меня смущает, учитывая, что, как написано, вы знаете CustomerId для обоих запросов, когда начинаете. Вы можете выполнить их параллельно, а затем возобновить, когда оба будут выполнены. Но я чувствую, что что-то упускаю. Также я не знаю, что означает отсутствие Foos. Ваш второй запрос, похоже, запрашивает все Foo в базе данных, которые не принадлежат целевому клиенту. Вы действительно это имеете в виду?   -  person Ward    schedule 11.12.2013
comment
Уорд, да. Я хочу загрузить сущность Customer. Затем я хочу найти все Foo, которых нет у экземпляра клиента, и поместить их в коллекцию экземпляров клиента. Что возвращать, где я запуталась и код стал нечитаемым. Я дополню вопрос лучшим примером.   -  person user210757    schedule 11.12.2013
comment
Что делать, если вы запрашиваете Customer с ID = 1, а есть Foos, принадлежащие Customer с ID = 2. Эта логика возьмет все Foos для клиента №2 и переместит их в клиент №1. Это действительно то, что вы намереваетесь? Обратите внимание, что ваш первый запрос не извлекает Foos, которые уже принадлежат клиенту №1. Таким образом, в конце операций, которые вы запрашиваете, кэш-память Customer1.Foos не будет возвращать никаких Foo, которые раньше принадлежали клиенту № 1, и все Foo, которые раньше принадлежали любому другому клиенту. Я не понимаю.   -  person Ward    schedule 12.12.2013
comment
Нет проблем - я попытался сделать свой пример слишком абстрактным. Думаю, я разобрался - Q.defer был тем, что мне было нужно. Спасибо за помощь; Закрою этот вопрос.   -  person user210757    schedule 12.12.2013
comment
Q.defer? Я не знаю, понадобится ли мне это для всего, что звучит как ваш случай. Смотри мой ответ   -  person Ward    schedule 12.12.2013
comment
Уорд, это были отношения «многие ко многим», поэтому Customer не владеет Foos, это скорее атрибуты, которые можно добавить к любому клиенту, и я хотел добавить те, которых у них не было при загрузке клиента. Мне действительно было трудно понять, как объединить функции в цепочку, возвращая обещания - я думаю, в основном из-за борьбы с набором текста в машинописном тексте, и в итоге я использовал отложенный вариант под условными выражениями? комментарий. Я не видел твоего ответа. В любом случае, я не очень хорошо понял свой вопрос.   -  person user210757    schedule 13.12.2013
comment
Ward FYI - я добавил свое решение внизу. Кажется, мне нужен Q.defer из-за моего условного выражения, как еще я мог бы получить возвращаемое значение. Может, есть способ лучше без отсрочки?   -  person user210757    schedule 13.12.2013


Ответы (1)


Я возьму ваш пример за чистую монету, несмотря на мою неспособность понять семантику ... в частности, мою неспособность понять, почему получение всех Foo, не принадлежащих клиенту, будет полезным.

Я просто сосредоточусь на «цепочке» и предполагаю, что вы хотите, чтобы вызывающий абонент завладел выбранным клиентом, когда вы закончите.

Последовательная цепочка

В этом примере мы ждем клиента перед тем, как получить Foos

function getCustomer(custId) {

    var cust;
    var em = this.em;
    var query = breeze.EntityQuery().from("Customers")
                .where("CustomerId", "==", custId);

    // On success calls `gotCustomer` which itself returns a promise
    return em.executeQuery(query)
        .then(gotCustomer)
        .fail(handleFail); // you should handleFail 

    // Called after retrieving the customer.
    // returns a new promise that the original caller will wait for.
    // Defined as a nested success function 
    // so it can have access to the captured `cust` variable 
    function gotCustomer(data) {
       cust = data.results[0];

       if (!cust) {
          return null; // no customer with that id; bail out now
       }
       // got a customer so look for non-customer foos
       // returning another promise so caller will wait
       return breeze.EntityQuery().from("Foos")
              .where("CustomerId", "!=", custId)
              .using(em).execute()
              .then(gotFoos);
    }

    // Now you have both the customer and the other Foos;
    // bring them together and return the customer.
    function gotFoos(data) {
        var foos = data.results;
        // assume `notMyFoos` is an unmapped property that 
        // should hold every Foo that doesn't belong to this Customer
        foos.forEach(function(f) { cust.notMyFoos.push(f); }
        return cust; // return the customer to the caller after Foos arrive.
    }
}

Параллельные асинхронные запросы

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

function getCustomer(custId) {
    var em = this.em;

    var custPromise = breeze.EntityQuery().from("Customers")
                      .where("CustomerId", "==", custId)
                      .using(em).execute();

    var fooPromise = breeze.EntityQuery().from("Foos")
                      .where("CustomerId", "!=", custId)
                      .using(em).execute();

    Q.all([custPromise, fooPromise])
      .then(success)
      .fail(handleFail); // you should handleFail

    // Now you have both the customer and the "other" Foos;
    // bring them together and return the customer.
    // `data` is an array of the results from each promise in the order requested.
    function success(data) {
        var cust = data[0].results[0];
        if (!cust) return null;

        var foos = data[1].results;

        // assume `notMyFoos` is an unmapped property that 
        // should hold every Foo that doesn't belong to this Customer
        foos.forEach(function(f) { cust.notMyFoos.push(f); }
        return cust; // return the customer to the caller after Foos arrive.
    }
}

Обратите внимание, что мне не нужно делать так много проверки на null в путях успеха. У меня гарантированно будет data.results при вызове успешного обратного вызова. Я действительно должен учитывать возможность того, что Customer не совпадает с custId.

person Ward    schedule 14.12.2013
comment
Я думаю, что я боролся с последовательным решением, потому что я пытался сделать это без переменной cust, доступной для двух функций. Но кого это волнует, ваше параллельное решение намного лучше! - person user210757; 16.12.2013
comment
Это может быть лучше. У него есть дефект, заключающийся в том, что он получает Foos, даже если нет клиента с данным custId. Вот почему я добавил примечание о высокой вероятности того, что запрос клиента вернет клиента. Ваш пользовательский интерфейс может сделать эту вероятность очень высокой (например, выбор из списка), и в этом случае параллелизм может быть полезен для вашего приложения. Однако, когда дело доходит до производительности, я ничего не предполагаю. Так много теоретических аргументов оказываются неверными. Протестировать и проверить. Повеселись! - person Ward; 16.12.2013