0

In an extension, I'm trying to fetch an array of local resources and pass them from the page script of a local HTML file, to its content script, to the background script, and ultimately to an extension page for display. It starts in the page script because the resources are local.

If one resource is fetched, it works without error and the extension page displays the resource. When the length of the array of resources is greater than one, it works only if the fetch requests are written out as if the length was known in advance. If array.map is used to convert the array of resource paths to an array of fetches to be used in a Promise.all, the fetch data is empty when it reaches the background script, although all the logs in the page and content script display the files.

The code is below. con_script.return_fetch is a function in the content script placed on the page script's window using cloneInto to pass the data to the background script. o is just an object of two properties, src the array of source paths and caller the tab.id of the tab with the extension page loaded that initiated the event requesting to display the resources. The Promise.all adds a third property status depending upon whether the set all resolves or not.

If o.src is written to the console from this page script or the associated content script, it is an array of blobs as it should be. But in the background script it contains the paths instead of the blobs. This only happens if use Array.map.

Why would using Array.map result in the changes to object o not being recognized in the background script? I'm repeating myself, but the exact same code works correctly if the commented out Promise.all is run instead. But, of course, the number of resources in each request is unknown.

Is there another method in addition to Array.map to convert the array of sources to an array of fetch promises?

Thank you.

Promise.all( o.src.map( ( v, i ) => { fetch_i( v, i ); } ) )
//Promise.all( [ fetch_i( o.src[ 0 ], 0 ), fetch_i( o.src[ 1 ], 1 ) ] )

  .then( ( r ) => 
    { 
      o.status = 'resolve';
      window.con_script.return_fetch( o ); 
    } )
  .catch( ( e ) =>
    { 
      o.status = 'reject';
      o.src = e;
      window.con_script.return_fetch( o ); 
    } );

function fetch_i( v, i )
  {
    return new Promise( ( resolve, reject ) =>
      {     
        fetch( v )
          .then( response => response.blob() )
          .then( ( myBlob ) =>
            { 
              o.src[ i ] = myBlob; // Overwrite the source path with the blob.
              resolve(); 
            } )
          .catch( ( e ) =>
            { 
              reject( e );
            } );
      } ); // close promise
  } // close fetch_i
Gary
  • 2,393
  • 12
  • 31
  • 1
    It's not the problem (I don't think), but that code is falling prey to the [explicit promise creation anti-pattern](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it). There's no reason for `fetch_i` to use `new Promise`. – T.J. Crowder Sep 03 '20 at 06:28
  • It's also really odd to have the callbacks replace entries in the original array. The normal thing to do would be to use the new array you get from the `Promise.all` fulfillment handler. – T.J. Crowder Sep 03 '20 at 06:29
  • 1
    That code is also being hit by the `fetch` API footgun: `fetch` only rejects its promise on *network* errors, not HTTP errors, so you need to check whether the HTTP request worked in the first fulfillment handler ([more here](http://blog.niftysnippets.org/2018/06/common-fetch-errors.html)). Here's a version addressing all of those issues: https://pastebin.com/seZtgNNK This isn't an answer because I can't see why any of those would result in what you're seeing. It's possible one of them would, but I can't point to anything and say "That's why!" :-) – T.J. Crowder Sep 03 '20 at 06:33
  • 3
    Remove curly braces in `{ fetch_i( v, i ); }`. – wOxxOm Sep 03 '20 at 06:42
  • Thank you. I see what you mean about the `promise` in `fetch_i`; I don't know why I did that. Regarding your second comment, I tried `resolve(myBlob)` and the result was the same. I'll correct these other items and see what happens. Thanks again. – Gary Sep 03 '20 at 06:43
  • 1
    Since you used a code block in the map() function you are missing a `return` so effectively what you think is an array of promises is an array of `undefined` that resolves immediately and before the requests have resolved – charlietfl Sep 03 '20 at 06:45
  • 1
    @T.J.Crowder Thanks again for those links. They were very helpful. I know now why I returned a `new Promise` in `fetch_i`; it's because I didn't understand what I was really doing there but that information helped me to understand. – Gary Sep 03 '20 at 07:26
  • @wOxxOm - Doh! I read right past it. :-D – T.J. Crowder Sep 03 '20 at 07:37
  • Gary - Glad to help! Since the fundamental problem was what @wOxxOm pointed out, I've closed this as being answered by [this](https://stackoverflow.com/questions/45754957/why-doesnt-my-arrow-function-return-a-value). Happy coding! – T.J. Crowder Sep 03 '20 at 07:38

0 Answers0