Синхронная или последовательная выборка в Service Worker

Мне нужно отправить серию запросов PUT и POST от Service Worker. Порядок, в котором они отправляются, имеет значение.

Требования:

  • Учитывая метод запроса, URL-адрес и тело JSON, отправьте запрос
  • If it succeeds (response.status < 300):
    • Pass body to a success function
    • Вызов следующего запроса в очереди
  • If it fails:
    • Pass responseText or err to error function
    • Остановить выполнение

Если я просто перебираю очередь и вызываю fetch для каждого запроса, дисперсия сети может (часто так и происходит) привести к тому, что запросы поступят на сервер не по порядку.

Как сделать цепочку из fetch запросов, где каждый результат зависит от успеха предыдущего?


Что я пробовал:

  • Вместо этого XHR (при условии, что я мог бы использовать «async: false», но это не разрешено в Service Worker).
  • setTimeout(sendRequest, i*200). Взлом, не надежный.
  • Promise loops на основе этих примеров Шаблоны промисов ES6. Это казалось наиболее многообещающим, но примеры приведены для простого случая, когда предполагается успех. Не могу заставить его работать с выборкой.

Контекст: я использую "исходящие" запросы API для поддержки автономного чтения, создания и обновления данных. Работает хорошо, за исключением этой проблемы с заказом.


person AdrianoFerrari    schedule 08.09.2015    source источник
comment
Примечание. Я использую собственные обещания ES6, а повторяющийся вопрос относится к другим библиотекам.   -  person AdrianoFerrari    schedule 08.09.2015


Ответы (2)


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

var workQueue = [work, goes, here];
var currentItem = workQueue.shift();
return performWorkWith(currentItem)
         .then(handleResponseWithQueue(workQueue));

function handleResponseWithQueue(queue) {
  return function handleResponse(response) {
      if (response.ok && queue.length > 0)
        return performWorkWith(queue.shift()).then(handleResponseWithQueue(queue));
  };
}

Вы можете обобщить этот шаблон на (упрощенный):

function series(work, queue) {
  if (queue.length <= 0) return;
  work(queue.shift()).then(function() {
    if (queue.length > 0) return series(work, queue);
  });
}
person Sean Vieira    schedule 08.09.2015
comment
Спасибо @sean-vieira! Оказывается, я был настолько захвачен своим замешательством по поводу промисов, что не следил за тем, чтобы сама очередь очищалась по мере ее обработки. - person AdrianoFerrari; 08.09.2015
comment
Я использовал аналогичный подход, просто рекурсивную функцию, которая брала индекс массивов промисов и запускала их в этой позиции. На самом деле вы можете создать некоторую пользовательскую логику всякий раз, когда вы хотите запустить следующую или нет. - person Alberto S.; 14.05.2019

Я думаю, вы захотите следовать шаблону цикла синхронизации из на странице Шаблоны обещаний ES6.

Как только ваша цепочка обещаний «успех» настроена через .reduce(), вы можете добавить в конец одно предложение .catch() для обработки отчетов об ошибках. Любое отклонение обещания/throw внутри цепочки обещаний замкнет все .then() и перейдет прямо к вашему .catch() в конце.

Чтобы это работало так, как вы описываете, вам нужно явно проверить статус ответа HTTP с ошибкой в ​​​​ваших fetch(...).then(...) и throw, если вы столкнетесь с ним, поскольку в противном случае ответ об ошибке HTTP не вызовет .catch(). (Однако NetworkErrors или подобные исключения во время выполнения вызывают срабатывание .catch().) Что-то вроде:

fetch('https://example.com').then(response => {
  if (!response.ok) { // See https://fetch.spec.whatwg.org/#ok-status
    throw new Error('Invalid HTTP response: ' + response.status);
  }
  // Otherwise, do something with the valid response.
})
person Jeff Posnick    schedule 08.09.2015
comment
Спасибо за это, @jeff-posnick. Я не понимал, что не выдавал ошибку, так что это было частью проблемы. Однако я отметил другой как ответ, потому что большая проблема заключалась в том, что я не очищал саму очередь! - person AdrianoFerrari; 08.09.2015