Подтвердить что ты не робот

Цепочка Promises рекурсивно

Я работаю над простым приложением Windows 8, в котором мне нужно получить набор данных с веб-сайта. Я использую WinJS.xhr() для извлечения этих данных, которые возвращают Promise. Затем я передаю обратный вызов в этот метод Promise.then(), который поставляет мой обратный вызов с возвращаемым значением из асинхронного вызова. Метод .then() возвращает другое обещание, предоставляя ему значение, возвращаемое моим обратным вызовом. Основная структура такого запроса будет следующей:

WinJS.xhr({ url: "http://www.example.com/" }).then(
    function callback( result_from_xhr )
    {
        //do stuff
        return some_value;
    }).then(
    function secondcallback( some_value )
    {
        //do stuff
    });

Однако в моей ситуации мне может потребоваться дополнительные запросы для данных в зависимости от данных, возвращаемых первым запросом, и, возможно, больше запросов в зависимости от данных THAT... и т.д., рекурсивно.

Мне нужен способ закодировать это так, что final.then() не будет выполняться до тех пор, пока ВСЕ рекурсии не будут завершены, аналогично этому:

function recurse() {
    return WinJS.xhr({ url: "http://www.example.com/" }).then(
        function callback( result_from_xhr )
        {
            if( result_from_xhr == something )
            {
               recurse();
            }
        });
}

recurse().then(
function final()
{
    //finishing code
});

Проблема состоит в том, что, конечно, код завершения вызывается, как только завершается первый уровень рекурсии. Мне нужно каким-то образом вложить новое обещание и старое обещание в рамках обратного вызова.

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

4b9b3361

Ответ 1

Что бы я хотел сделать, это создать совершенно новое, автономное обещание, которое вы можете выполнить вручную, и вернуть его из функции recurse(). Затем, когда вы натолкнулись на то, что знаете, что вы сделали асинхронную работу, выполните это обещание.

Promise.join работает, когда у вас есть известный набор promises - вам нужен весь массив promises, доступный до вызова соединения. Если бы я выполнил исходный вопрос, у вас есть переменное число promises, более вероятно, появившееся как часть работы async. Присоединиться не является правильным инструментом в этих обстоятельствах.

Итак, как это выглядит? Что-то вроде этого:

function doSomethingAsync() {
  return new WinJS.Promise(function (resolve, reject) {
    function recurse() {
      WinJS.xhr({ url: "http://www.example.com/" })
        .then(function onResult(result_from_xhr) {
          if (result_from_xhr === something) {
            recurse();
          } else {
            // Done with processing, trigger the final promise
            resolve(whateverValue);
          },
          function onError(err) {
            // Fail everything if one of the requests fails, may not be
            // the right thing depending on your requirements
            reject(err);
          });
    }
    // Kick off the async work
    recurse();
  });
}

doSomethingAsync().then(
function final()
{
    //finishing code
});

Я перегруппировался там, где происходит рекурсия, чтобы мы не воссоздавали новое обещание каждый раз, поэтому вложенная функция recurse() вместо того, чтобы иметь ее на внешнем уровне.

Ответ 2

Я решил эту проблему, возможно, по-другому (я думаю, это та же проблема), создав собственное приложение для Windows 8.

Причина, по которой я столкнулся с этой проблемой, состоит в том, что по умолчанию запрос к таблице будет разбиваться на страницы и возвращать только 50 результатов за раз, поэтому я создал шаблон, чтобы получить первые 50, а затем следующие 50, и т.д., пока ответ не вернется с менее чем 50 результатами, а затем разрешит обещание.

Этот код не будет реальным кодом, просто для иллюстрации:

function getAllRows() {
    return new WinJS.Promise(function(resolve, reject){

        var rows = [];

        var recursivelyGetRows = function(skipRows) {
            table.skip(skipRows).read()
                .then(function(results){
                    rows = rows.concat(results);

                    if (results.length < 50) {
                        resolve(rows);
                    } else {
                        recursivelyGetRows(skipRows + 50);
                    }
                })
        }

        recursivelyGetRows(0);

    });
}

поэтому getAllRows() возвращает обещание, которое разрешается только после того, как мы получим результат с менее чем 50 результатами (что указывает на последнюю страницу).

В зависимости от вашего варианта использования вы, вероятно, захотите также бросить обработчик ошибок.

Если неясно, "таблица" - это таблица мобильных служб.

Ответ 3

Вам понадобится использовать шаблон Promise.join(). done(). Передайте массив Promises для join(), который в вашем случае будет набором вызовов xhr. Команда join() вызывается только done(), когда все xhr Promises завершены. Вы получите массив результатов, переданных в done(), который затем вы можете перебрать и начать с нового вызова Promise.join(). Done(). Вещь, которая должна быть в стороне от использования этого подхода, заключается в том, что если один из Promises, переданных в join(), не работает, вся операция рассматривается как условие ошибки.

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

Ответ 4

Ну, я решил свою проблему; моя рекурсивная функция неверно интерпретировала данные и, таким образом, никогда не переставала рекурсивно. Благодарим вас за помощь, и я обязательно буду смотреть эти скринкасты, поскольку я до сих пор не совсем понимаю структуру цепей Promise.