Не удалось проанализировать поток json с помощью node-fetch

avatar
icelemon
9 августа 2021 в 00:55
301
1
0

Я запускаю пример кода из функции потока node-fetch. Иногда он может успешно разобрать фрагмент, иногда возвращает сообщение об ошибке SyntaxError: Unexpected token { in JSON at position 312

const fetch = require('node-fetch');

async function main() {
  const response = await fetch('https://httpbin.org/stream/3');
  try {
    for await (const chunk of response.body) {
      console.log(JSON.parse(chunk.toString()));
    }
  } catch (err) {
    console.error(err.stack);
  }
}

main()

Кто-нибудь знает почему? Могу ли я положиться на фрагмент?

Источник

Ответы (1)

avatar
waterloos
9 августа 2021 в 04:23
1

Запрашивая https://httpbin.org/stream/3, сервер отправляет данные, разделенные на 3 части, через поток. Клиент (в данном случае скрипт вашего узла) поддерживает связь с сервером, продолжает получать данные и разбивает их на части.

node-fetch просто разбивает данные на фрагменты каждый раз, когда выполняется одна асинхронная задача, как вы можете видеть здесь: строка 199 body.js.

Поэтому, если разделенные данные поступают так быстро, что асинхронная задача получает несколько блоков данных в цикле событий одного узла, node-fetch получает несколько данных Джейсона.

Вот тогда и возникает ошибка. Запустите следующий код с добавлением console.log. Затем вы можете подтвердить, что при возникновении ошибки несколько объектов jason сохраняются в chunk.

.
const fetch = require('node-fetch');

async function main () {
  const response = await fetch('https://httpbin.org/stream/3');
  try {
    for await (const chunk of response.body) {
      console.log("------chunk-----\n", chunk.toString());
      console.log("Char at 310 -- 315", chunk.toString().substring(310, 315));
      console.log(JSON.parse(chunk.toString()));
    }
  } catch (err) {
    console.error(err.stack);
  }
}

main()

Для этого сайта вы можете разделить данные самостоятельно, когда возникает следующая ошибка.

const fetch = require('node-fetch');

async function main () {
  const response = await fetch('https://httpbin.org/stream/3');
  try {
    for await (const chunk of response.body) {
      try {
        console.log(JSON.parse(chunk.toString()));
      } catch (_) {
        console.log("------ Handling multiple chunks ------");
        chunk.toString().split("\n").slice(0, -1).forEach(d => console.log(JSON.parse(d)));
      }
    }
  } catch (err) {
    console.error(err.stack);
  }
}
main()

При использовании Fetch API с браузером вы можете написать свой собственный ReadableStreamReader и реализовать стратегию обработки разделенных данных.

Обновление:

Вы можете просто использовать библиотеку stream-json jsonl Parser следующим образом:

const { parser: jsonlParser } = require('stream-json/jsonl/Parser');

async function main () {
  const response = await fetch('https://httpbin.org/stream/5');
  response.body.pipe(jsonlParser())
    .on('data', ({ key, value }) => console.log(value))
    .on('end', () => console.log("Parsing done."))
    .on('error', err => console.log(err.message));
}

main();