Проблема вызывая рекурсивное обещание, когда обещание не возвращается во второй раз - PullRequest
0 голосов
/ 07 февраля 2020

Я пытаюсь рекурсивно извлечь данные из API, который возвращает данные в страницах.

Я хочу продолжать извлекать данные и добавлять результат в массив, пока hasNextPage не станет ложным.

Вот код, с которым я до сих пор связан:

function getPhaseData(cursor) {
  let cursorAddition = "";
  if (cursor === undefined) {
    cursorAddition = "";
  } else {
    cursorAddition = ' after: "' + cursor + '"';
  }
  // build the API request - have no cursor/starting position on the initial calls, but add it on the subsequent calls

  let phaseData = [];
  // eventually want to return this variable with all the data from the API

  rp({
    url: "https://app.pipefy.com/queries",
    method: "POST",
    headers: {
      Authorization: "Bearer " + process.env.PIPEFY_BEARER,
      "Content-Type": "application/json"
    },
    mode: "no-cors",
    body:
      '{"query":"{ phase(id: 2087517) {id name cards_count cards (first: 30' +
      cursorAddition +
      ') {  pageInfo { endCursor hasNextPage } edges { node { id title due_date } } } }}"}'
  }).then(function(response) {
    let responseData = JSON.parse(response);

    for (var i in responseData.data.phase.cards.edges) {
      phaseData.push(responseData.data.phase.cards.edges[i]); // add the data from the first call to the phaseData array
    }

    if (responseData.data.phase.cards.pageInfo.hasNextPage) { // if next page exists
      // call function again to get the next page, starting from the cursor position
      return getPhaseData(responseData.data.phase.cards.pageInfo.endCursor).then(function(nextResponse) {
        let nextResponseData = JSON.parse(nextResponse);
        for (var i in nextResponseData.data.phase.cards.edges) {
        phaseData.push(nextResponseData.data.phase.cards.edges[i]); // add the data from the subsequent calls to the phaseData variable
      }
      });
    } else {
      return phaseData; // if no subsequent page exists, just return the phase data
    }
  });
}

getPhaseData();

У меня проблема в том, что:

На последней итерации, когда hasNextPage false, функция возвращает массив вместо обещания.

Это замечательно, но на предпоследней итерации мы обнаруживаем, что hasNextPage равен true, и снова вызываем функцию. Однако мы не можем сделать .then() для этого результата, поскольку вместо обещания возвращается массив!

Функция останавливается на одну страницу меньше, чем должна быть.

ошибка: Unhandled rejection TypeError: Cannot read property 'then' of undefined

Буду признателен за любые рекомендации, особенно в отношении шаблонов проектирования.

Я вижу, в чем проблема, но как я могу это исправить?

1 Ответ

2 голосов
/ 07 февраля 2020

В сущности, у вас фактически есть:

function getPhaseData(cursor) {
    // synchronous stuff
    rp({ ... })
    .then(function(response) {
        let phaseData = someTransformOf(response);
        if (testForNextPage) {
            return getPhaseData(response.someProperty)
            .then(function(nextResponse) {
                phaseData = phaseData.concat(someTransformOf(nextResponse));
            });
        } else {
            return phaseData;
        }
    });
}

Несколько проблем:

  1. getPhaseData() возвращает неопределенное значение, поскольку перед * 1009 нет return *
  2. внутренний .then возвращает неопределенное значение, поэтому каждый уровень рекурсии (попытки) доставляет undefined к предыдущему уровню.
  3. Преобразование ответа необходимо выполнить один раз, тогда во внешнем. Затем внутренний должен получать преобразованные данные со следующего нижнего уровня рекурсии, а не необработанный ответ, который нуждается в преобразовании.

На самом деле вы на 90% - исправления довольно просты:

function getPhaseData(cursor) {
    // synchronous stuff
    return rp({ ... })
 // ^^^^^^ 
    .then(function(response) {
        let phaseData = someTransformOf(response);
        if (testForNextPage) {
            return getPhaseData(response.someProperty)
            .then(function(nextResponse) {
                return phaseData.concat(nextResponse); // no need to perform the transform here, it was performed by the recursive getPhaseData() call
             // ^^^^^^
            });
        } else {
            return phaseData;
        }
    });
}

Итак,

  • getPhaseData() возвращает Promise, независимо от того, был ли он вызван внутренне или внешне.
  • , каждый уровень рекурсии доставляет объединение данных из все нижние уровни.
  • верхний уровень рекурсии предоставляет исходному вызывающему объекту полную конкатенацию всех данных всех нижних уровней (ie все страницы).
...