1

I'm making a Google Chrome extension. I'm trying to open new tabs and group them. I have urls as an array. But chrome.tabs.group function doesn't wait until all tabs open.

var ourTabIds = []
for(const url of urls) {
    chrome.tabs.create({active: false, url : url},tab => {
         ourTabIds.push(tab.id)
    })
}
chrome.tabs.group({tabIds : ourTabIds}, groupId => {console.log(groupId)})

The group function works when the ourTabIds is still empty. So it gives errors.

Why it doesn't wait? How can I fix that?

I2mNew
  • 123
  • 11
  • Does this answer your question? [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – Heretic Monkey Mar 26 '21 at 11:42
  • 1
    Add code in the `create` callback that compares `ourTabIds.length` with `urls.length` and calls `group` when they are equal. – Heretic Monkey Mar 26 '21 at 11:44

3 Answers3

1

You could promisify chrome.tabs and then use Promise.all

function createTab(input){
   return new Promise(resolve => {
     chrome.tabs.create(input,tab => resolve(tab))
   });
}

and then you code becomes

(async function doIt(){
   var tabs = await Promise.all(urls.map(url => createTab({active: false, url : url})));

   chrome.tabs.group({tabIds : tabs.map(t => t.id)}, groupId => {console.log(groupId)})

})()
Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • It's more cool solution yes but I have to understand, normally when I write a loop, does not the code below the loop wait until the loop finish? – I2mNew Mar 26 '21 at 12:09
  • @I2mNew in your original code, no. Because the call you're making is asynchronous (see the link under your question someone posted). In this code, yes the 2nd line (the group line) waits for the first to complete – Jamiec Mar 26 '21 at 12:10
  • 1
    @I2mNew It does "wait" until the loop finishes. The thing is that your loop finishes long before your callbacks to the `create` method runs because `chrome.tabs.create()` is an asynchronous process. That's the whole reason it's using callbacks in the first place. – Lennholm Mar 26 '21 at 12:15
  • @Lennholm That's what I was trying to say, thanks for being more succinct than me. – Jamiec Mar 26 '21 at 12:17
0
var ourTabIds = [];
var countTabs = 0;
    for(const url of urls) {
            chrome.tabs.create({active: false, url : url},tab => {
                 ourTabIds.push(tab.id)
            countTabs++;
           if (countTabs==urls.length){
              chrome.tabs.group({tabIds : ourTabIds}, groupId => {console.log(groupId)})
            }
          })
        }
        
Nicolas I
  • 234
  • 4
  • 15
0

The function chrome.tabs.create() uses a callback as parameter. You should either convert that to Promises and finish it all with .then() or check if it is the last url (changing from of to in)

var ourTabIds = []
for(const index in urls) {
    chrome.tabs.create({active: false, url : urls[index]}, tab => {
         ourTabIds.push(tab.id)
         if (index === urls.length - 1 ) { // last url 
    //call when EVERYTHING has been pushed by the callback                 
chrome.tabs.group({tabIds : ourTabIds}, groupId => {console.log(groupId)}) 
             }
        })
    }
iwaduarte
  • 1,600
  • 17
  • 23
  • Yes this solution and the other solutions that use length of links works, thank you. But I can't understand why my first code doesn't wait? – I2mNew Mar 26 '21 at 11:57
  • It is not about waiting. It is not how callback works. See: https://developer.mozilla.org/en-US/docs/Glossary/Callback_function – iwaduarte Mar 26 '21 at 12:11