从递归获取中解包所有承诺

unwrapping all promises from a recursive fetch

我正在尝试创建一个递归函数,returns 给定块的所有子块。

关于一些背景信息,我正在使用概念 API 从页面中获取所有块。 一个块可以有块的子块,这些子块也可以有块的子块。 这就是为什么我需要一个递归函数来检查所有块。

其实我已经成功的从google appscript做了一个递归函数,如下


// recursive function to get all nested blocks in a flattened array format
function getSubBlocks(blockId) {
  const url = `https://api.notion.com/v1/blocks/${blockId}/children?page_size=100`
  let options = {
    "async": true,
    "crossDomain": true,
    "method": "get",
    "headers": {
      "Authorization": `Bearer ${NOTION_API_KEY}`,
      "Notion-Version": "2022-02-22",
      "Content-Type": "application/json"
    }
  };

  var response = JSON.parse(UrlFetchApp.fetch(url, options).getContentText());;
  console.log(response)
  let blocks = response.results

  Utilities.sleep(50)

  // guard clause
  if (blocks.length == 0) {
    return
  }

  blocks = blocks.concat(blocks.map((block) => getSubBlocks(block.id)).flat())
  // get rid of undefined
  blocks = blocks.filter(block => block != undefined)
  return blocks
}

我正在尝试从 node.js 中创建一个相同的函数,但我在展开所有块时遇到了问题。 下面是递归函数的node-js版本

import fetch from "node-fetch";

// recursive function to get all nested blocks in a flattened array format
async function getSubBlocks(blockId) {
  const url = `https://api.notion.com/v1/blocks/${blockId}/children?page_size=100`;
  let options = {
    async: true,
    crossDomain: true,
    method: "get",
    headers: {
      Authorization: `Bearer ${NOTION_API_KEY}`,
      "Notion-Version": "2022-02-22",
      "Content-Type": "application/json",
    },
  };

  const response = await fetch(url, options);
  const r = await response.json();
  let blocks = r.results;
  // guard clause ends the function if the array is empty
  if (blocks.length == 0) {
    return;
  }
  // for each block objects, check for children blocks in a recursive manner

  let newBlocks = await blocks
    .map(async (block) => await getSubBlocks(block.id))
    .flat();
  blocks = blocks.concat(newBlocks);

  // get rid of undefined
  blocks = blocks.filter((block) => block != undefined);
  return await blocks;
}

getSubBlocks(testBlock)
  .then((r) => console.log(r))
  .catch((error) => console.log(error));

我明白上面的函数是一个异步函数,所以它总是returns一个承诺。 这就是为什么我试图用 then 子句解包 promise,但我只解包了第一个块,而递归调用的其他块仍然作为 promise 呈现。 示例输出如下:

[
  {
    object: 'block',
    id: 'XXXXXXXXXXXX',
    created_time: '2022-05-28T05:15:00.000Z',
    last_edited_time: '2022-05-28T05:15:00.000Z',
    created_by: { object: 'user', id: 'XXXXXX' },
    last_edited_by: { object: 'user', id: 'XXXXXX' },
    has_children: false,
    archived: false,
    type: 'paragraph',
    paragraph: { rich_text: [Array], color: 'default' }
  },
  {
    object: 'block',
    id: 'XXXXXXXXXXXX',
    created_time: '2022-05-28T05:15:00.000Z',
    last_edited_time: '2022-05-28T05:15:00.000Z',
    created_by: { object: 'user', id: 'XXXXXXXXXXXX' },
    last_edited_by: { object: 'user', id: 'XXXXXXXXXXXX' },
    has_children: false,
    archived: false,
    type: 'paragraph',
    paragraph: { rich_text: [], color: 'default' }
  },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },

]

有什么办法可以解决这个问题吗?

从更广泛的意义上讲,在使用递归和获取时是否有任何最佳实践来检索所有值?

您的 getSubBlocks 函数不 return Promise
此外,您可能希望将方法从 map 更改为正常的 for...of 循环以简化代码并删除一些不必要的 await.

试试这个方法:

import fetch from 'node-fetch';

// recursive function to get all nested blocks in a flattened array format
async function getSubBlocks(blockId) {
  const url = `https://api.notion.com/v1/blocks/${blockId}/children?page_size=100`;
  let options = {
    async: true,
    crossDomain: true,
    method: 'get',
    headers: {
      Authorization: `Bearer ${NOTION_API_KEY}`,
      'Notion-Version': '2022-02-22',
      'Content-Type': 'application/json',
    },
  };

  const response = await fetch(url, options);
  const r = await response.json();

  let blocks = r.results;
  // guard clause ends the function if the array is empty
  if (blocks && blocks.length == 0) {
    return undefined;
  }

  // for each block objects, check for children blocks in a recursive manner
  for (const block of blocks) {
      const subBlocks = await getSubBlocks(block.id)
      if (subBlocks) blocks = [...blocks, ...subBlocks]
  }

  return blocks;
}

const res = getSubBlocks(testBlock)