2017-02-13 6 views
3

У меня есть вызов api, который иногда возвращает постраничные ответы. Я хотел бы автоматически добавить их в свои обещания, чтобы получить обратный вызов, как только все данные будут получены.Добавление обещания в Promise.all()

Это моя попытка. Я ожидаю, что новое обещание будет добавлено, а Promise.all - как только это будет сделано.

Что на самом деле происходит, так это то, что Promise.all не ждет второго запроса. Я предполагаю, что Promise.all придает «слушателям», когда он называется.

Есть ли способ «повторно инициализировать» Promise.all()?

function testCase (urls, callback) { 
    var promises = []; 
    $.each(urls, function (k, v) { 
     promises.push(new Promise(function(resolve, reject) { 
      $.get(v, function(response) { 
       if (response.meta && response.meta.next) { 
        promises.push(new Promise(function (resolve, reject) { 
         $.get(v + '&offset=' + response.meta.next, function (response) { 
          resolve(response); 
         }); 
        })); 
       } 
       resolve(response); 
      }).fail(function(e) {reject(e)}); 
     })); 
    }); 

    Promise.all(promises).then(function (data) { 
     var response = {resource: []}; 
     $.each(data, function (i, v) { 
      response.resource = response.resource.concat(v.resource); 
     }); 
     callback(response); 
    }).catch(function (e) { 
     console.log(e); 
    }); 
} 

Желаемая поток что-то вроде:

  1. Создать набор обещаний.
  2. Некоторые из обещаний порождают больше обещаний.
  3. Как только все начальные обещания и порожденные обещания разрешат, вызовите обратный вызов.
+0

Где же вы инициализация 'обещаний'? Я вижу, что ты подталкиваешь к нему, но я не вижу, чтобы ты его создал. –

+0

Что такое 'url'? (Если это массив, обычно это будет множественное число, например, 'urls'.) –

+0

Почему' response.resource = response.resource.concat (v.resource); '? Это каждый раз создает целый новый массив ...? –

ответ

3

Похоже, общей целью является:

  1. Для каждой записи в urls, вызовите $.get и ждать его завершения.
    • Если он возвращает только ответ без «рядом», держать что один ответ
    • Если он возвращает ответ с «рядом», мы хотим, чтобы просить «рядом», а затем сохранить их обоих ,
  2. Вызвать обратный вызов с помощью response, когда вся работа будет выполнена.

Я бы поменял # 2, чтобы вы только вернули обещание и разрешили его с помощью response.

Ключевой вещь обещаний, что then возвращает нового обещание, которое будет либо разрешенное с тем, что вы вернетесь из then (непосредственно, если вы возвращаетесь не-thenable значения, или косвенно, если вы возвращаете thenable, путем рабства к тому, что затем можно). Это означает, что если у вас есть источник обещаний ($.get, в данном случае), вам почти не нужно использовать new Promise; просто используйте обещания, которые вы создаете с помощью then. (. И catch)

Смотри комментарии:

function testCase(urls) { 
    // Return a promise that will be settled when the various `$.get` calls are 
    // done. 
    return Promise.all(urls.map(function(url) { 
     // Return a promise for this `$.get`. 
     return $.get(url) 
      .then(function(response) { 
       if (response.meta && response.meta.next) { 
        // This `$.get` has a "next", so return a promise waiting 
        // for the "next" which we ultimately resolve (via `return`) 
        // with an array with both the original response and the 
        // "next". Note that since we're returning a thenable, the 
        // promise created by `then` will slave itself to the 
        // thenable we return. 
        return $.get(url + "&offset=" + response.meta.next) 
         .then(function(nextResponse) { 
          return [response, nextResponse]; 
         }); 
       } else { 
        // This `$.get` didn't have a "next", so resolve this promise 
        // directly (via `return`) with an array (to be consistent 
        // with the above) with just the one response in it. Since 
        // what we're returning isn't thenable, the promise `then` 
        // returns is resolved with it. 
        return [response]; 
       } 
      }); 
    })).then(function(responses) { 
     // `responses` is now an array of arrays, where some of those will be one 
     // entry long, and others will be two (original response and next). 
     // Flatten it, and return it, which will settle he overall promise with 
     // the flattened array. 
     var flat = []; 
     responses.forEach(function(responseArray) { 
      // Push all promises from `responseArray` into `flat`. 
      flat.push.apply(flat, responseArray); 
     }); 
     return flat; 
    }); 
} 

Обратите внимание, как мы никогда не используем catch там; мы откладываем обработку ошибок вызывающему.

Использование:

testCase(["url1", "url2", "etc."]) 
    .then(function(responses) { 
     // Use `responses` here 
    }) 
    .catch(function(error) { 
     // Handle error here 
    }); 

testCase функция выглядит очень долго, но это только из-за замечаний.Здесь без них:

function testCase(urls) { 
    return Promise.all(urls.map(function(url) { 
     return $.get(url) 
      .then(function(response) { 
       if (response.meta && response.meta.next) { 
        return $.get(url + "&offset=" + response.meta.next) 
         .then(function(nextResponse) { 
          return [response, nextResponse]; 
         }); 
       } else { 
        return [response]; 
       } 
      }); 
    })).then(function(responses) { 
     var flat = []; 
     responses.forEach(function(responseArray) { 
      flat.push.apply(flat, responseArray); 
     }); 
     return flat; 
    }); 
} 

... и было бы еще более кратким, если бы мы использовали функцию стрелки ES2015 в. :-)


В комментарии вы просили:

Может ли это справиться, если был следующий следующий? Как страница 3 результатов?

Мы можем сделать это с помощью инкапсуляции, что логики в функцию мы используем вместо $.get, который мы можем использовать рекурсивно:

function getToEnd(url, target, offset) { 
    // If we don't have a target array to fill in yet, create it 
    if (!target) { 
     target = []; 
    } 
    return $.get(url + (offset ? "&offset=" + offset : "")) 
     .then(function(response) { 
      target.push(response); 
      if (response.meta && response.meta.next) { 
       // Keep going, recursively 
       return getToEnd(url, target, response.meta.next); 
      } else { 
       // Done, return the target 
       return target; 
      } 
     }); 
} 

Тогда наш главный testCase проще:

function testCase(urls) { 
    return Promise.all(urls.map(function(url) { 
     return getToEnd(url); 
    })).then(function(responses) { 
     var flat = []; 
     responses.forEach(function(responseArray) { 
      flat.push.apply(flat, responseArray); 
     }); 
     return flat; 
    }); 
} 
+0

Может ли этот дескриптор, если бы появился следующий 'следующий'? Как страница 3 результатов? – Josiah

+0

@Josiah: Он * может *, да. Нам придется изменить логику. Вероятно, мы хотим иметь функцию, обрабатывающую данный URL, и вернули обещание, которое будет разрешено с помощью массива ответов всех «ближайших», и использовать его рекурсивно. Вы хотите передать ему URL-адрес и смещение или подобное. –

+0

@Josiah: Оказывается, это очень просто сделать, я добавил его до конца ответа. –

1

Предполагая, что вы используете jQuery v3 +, вы можете использовать обещания, возвращенные $.ajax, чтобы перейти к Promise.all().

Что вам не хватает возвращается второй запрос как обещание вместо того, чтобы пытаться подтолкнуть его к массиву обещает

упрощенный пример

var promises = urls.map(function(url) { 
    // return promise returned by `$.ajax` 
    return $.get(url).then(function(response) { 
    if (response.meta) { 
     // return a new promise 
     return $.get('special-data.json').then(function(innerResponse) { 
     // return innerResponse to resolve promise chain 
     return innerResponse; 
     }); 

    } else { 
     // or resolve with first response 
     return response; 
    } 
    }); 

}) 

Promise.all(promises).then(function(data) { 
    console.dir(data) 
}).catch(function(e) { 
    console.log(e); 
}); 

DEMO

 Смежные вопросы

  • Нет связанных вопросов^_^