0

I'm developing a google chrome extension and I have to loop trough the nodes (folders) to check how many items I have within each folder. I'm suppling an item ID to the function getBookmarksCount(ID). I'm having an issue to get a result from the main function the console.log() returns correct value at the point of logging.

Here is my code:

const getBookmarksCount = (bmkNode) => {
    let nodes = []
    let result = 0

    new Promise ((resolve, reject) => {
        chrome.bookmarks.getChildren(bmkNode, (bmkChildren) => {

            _.each(bmkChildren, (item) => {
            
                // Check if the item is a bookmark link
                if (!(item.url === undefined || item.url === null)) {
                    nodes.push(item.title)
                }
                
            })
    
            resolve(_.size(nodes))

        })
        
    }).then((size) => {
        console.log(size) //The correct number of items is listed here eg. 6
        result = size
    })

    return result
}

//I'm suppling a parent folder ID the function should return number of children

getBookmarksCount(123) // eg. 6 -> at the moment returns 0

Here is my updated working version without Promise. setTimeout() is a dirty hack but works. Any suggestions how I can improve this function?

const getBookmarksCount = (bmkNode) => {
let nodes = []

const getChildrenCount = (bmkNode) => {

    chrome.bookmarks.getChildren(bmkNode, (bmkChildren) => {
        
        _.each(bmkChildren, (item) => {

            // if is bookmark otherwise loop trough subfolder
            (!(item.url === undefined || item.url === null)) ? nodes.push(item.title): getChildrenCount(item.id)

        })

    })

    setTimeout(() => {
        $(`#counter_${bmkNode}`).html(_.size(nodes))
    }, 50)

}

getChildrenCount(bmkNode)

}

// HTML Template

<label class="label label-primary" id="counter_${item.id}">0</label>
Cœur
  • 37,241
  • 25
  • 195
  • 267
Ben
  • 345
  • 2
  • 4
  • 13
  • 1
    Promises imply asynchronous code - you are not waiting for asynchronous code to complete before you `return result` – Bravo Nov 07 '18 at 04:48
  • chrome.bookmarks.getChildren is an async function so I'm trying fo wrap it in promise...How to improve my code? – Ben Nov 07 '18 at 04:56
  • p.s. instead of `_.each` and push, consider use of `_.filter` to extract the subset of legal array elements and `_.pluck` to get the title property of each. – Alnitak Nov 07 '18 at 05:14

1 Answers1

2

As pointed out by Bravo in the comments, you are not really waiting for your code to execute. You are ever so close, though!

const getBookmarksCount = (bmkNode) => {
   return new Promise ((resolve) => {
      let nodes = []
      chrome.bookmarks.getChildren(bmkNode, (bmkChildren) => {
          _.each(bmkChildren, (item) => {
              // Check if the item is a bookmark link
              if (!(item.url === undefined || item.url === null)) {
                  nodes.push(item.title)
              }  
          })

          resolve(_.size(nodes))
      })
   })
}
getBookmarksCount(123).then(size => {
    console.log(size)
})

Note the return new Promise on the second line, which is a key difference from your provided snippet. By doing this, you wait to "return" here until you actually finish the async work and call resolve. Then, to get the value returned, you would do the same .then syntax you used but at the call of getBookmarksCount.

Hopefully that helps and, of course, also works!

cgdibble
  • 72
  • 4
  • This way will work but `getBookmarksCount` is a part of the template `` so will be better if returns value without needing then... – Ben Nov 07 '18 at 05:18
  • 1
    @Ben that's the whole point - you _can't_ return a value from a function that makes asynchronous calls. – Alnitak Nov 07 '18 at 05:19
  • @cgdibble what do you think about my update? – Ben Nov 07 '18 at 06:42
  • 1
    @Ben it's a horrible nasty hack. – Alnitak Nov 07 '18 at 06:54
  • 1
    you've removed the template example you had before. Was that something built-in to Chrome plugins, or was it an external API like Angular, React, etc ? Angular knows how to take a `Promise` in its templates, and they auto update when the promise is resolved. – Alnitak Nov 07 '18 at 07:00
  • @Alnitak I've added template example – Ben Nov 07 '18 at 07:21
  • @Ben I wouldn't do it the hacky way, that will make it harder to understand and debug in the future for sure. Why is it better if it doesn't return a promise? That adds a new element to the question that changes it quite a bit from just returning with a promise. – cgdibble Nov 07 '18 at 14:35