Home > Mobile >  unwrapping all promises from a recursive fetch
unwrapping all promises from a recursive fetch

Time:05-29

I am trying to make a recursive function that returns all the subblocks of a given block.

For some background information, I am using the Notion API to get all the blocks from a page. A block can have a children of blocks, and those child blocks can also have a children of blocks. That is why I need a recursive function to check for all blocks.

Actually, I have already successfully made a recursive function from google appscript, as below


// 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
}

I am trying to make a identical function from node.js, but I have trouble in unwrapping all the blocks. The below is the node-js version of the recursive function

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));

I understand that the above function is an async function, so it always returns a promise. That is why I am trying to unwrap the promise with a then clause, but I only get the first blocks unwrapped, and the other blocks from the recursive calls are still presented as promises. An example output is as below:

[
  {
    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> },

]

Is there any way to solve this issue?

In a more broader sense, is there any best practice to retrieve all values when using recursion and fetch?

CodePudding user response:

Your getSubBlocks function does not return a Promise.
Also, you may want to change your approach from map to a normal for...of loop to simplify your code and remove some unnecessary await.

Try with this approach:

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)
  • Related