Работает ли fetch-API ReadableStream с promis.then (...)? - PullRequest
0 голосов
/ 12 марта 2019

Я пытаюсь использовать читаемые потоки Fetch API для загрузки нескольких файлов.

const files = [.....]; // my file objects
const promises = [];
for (let i = 0; i < files.length; i += 1) {
  const file = files[i];
  promises.push(
    fetch(file.uri)
    .then(response => {
      const reader = response.body.getReader();

      return new ReadableStream({
        async start(controller) {
          while (true) {
            const { done, value } = await reader.read();
            // When no more data needs to be consumed, break the reading
            if (done) {
              break;
            }

            if (!file.content) {
              file.content = value;
            }
            else {
              file.content += value;
            }
            // Enqueue the next data chunk into our target stream
            controller.enqueue(value);
          }
          // Close the stream
          controller.close();
          reader.releaseLock();
        }
      });
    })
    .then(rs => new Response(rs))
  );
}
return Promise.all(promises).then(() => {
  // do something else once all the files are downloaded
  console.log('All file content downloaded');
});

Идея состоит в том, чтобы файловые объекты имели только URI. Затем этот код добавит поле содержимого. После этого я могу сделать что-то еще.

Однако на практике кодовая последовательность неверна. А именно следующие строки идут сразу после друг друга.

return new ReadableStream({...});
console.log('All file content downloaded');

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

while (true) {

Код цикла. т.е. где содержимое файла передается в потоковом режиме.

Я, очевидно, неправильно понимаю фундаментальную концепцию. Как я могу ждать загрузки содержимого файла и , а затем сделать что-то еще? то есть как потоки работают с моделью promise.then ().

1 Ответ

0 голосов
/ 12 марта 2019

Нашел самое простое решение - создать собственное обещание.

const files = [.....]; // my file objects
const promises = [];
for (let i = 0; i < files.length; i += 1) {
  const file = files[i];
  promises.push(
    fetch(file.uri)
    .then(response => {
      return new Promise((resolve, reject) => {
        const reader = response.body.getReader();
        const stream = new ReadableStream({
          async start(controller) {
            while (true) {
              const { done, value } = await reader.read();
              // When no more data needs to be consumed, break the reading
              if (done) {
                break;
              }

              if (!file.content) {
                file.content = value;
              }
              else {
                file.content += value;
              }
              // Enqueue the next data chunk into our target stream
              controller.enqueue(value);
            }
            // Close the stream
            controller.close();
            reader.releaseLock();
          }
        });
      });
    })
    .then(rs => new Response(rs))
  );
}
return Promise.all(promises).then(() => {
  // do something else once all the files are downloaded
  console.log('All file content downloaded');
});

Предостережение: сценарий ошибки также должен быть корректно обработан.

...