1

I'm new to Javascript and am trying to get Images to dynamically add themselves to 'addCache'.

I will then use this with a service worker to add it to the cache.

My sw.js file looks like this:

    var product = [];
    fetch('http://localhost:3000/api/products')
    .then(res => res.json())
    .then(json => product = json)


//Caching Files


  var cacheName = 'pwa-v1';
  var toCache= [
      '/PW1/',
      '/PW1/Apps/calculate.js',
      '/PW1/Apps/login.js',
      '/PW1/Apps/index.js',
      '/PW1/index.html',
      '/PW1/products.html',
      '/PW1/style.css',

];


var productImages = [];

for(var i=0;i<product.length;i++){
    productImages.push('/PW1/img/'+product[i].name+'.jpg');
}
  var addCache = toCache.concat(productImages);

self.addEventListener('install', function(s) {

.............
.............
.............

My goal is to make the fetch request async, it fetches data from backend and stores it into the product array. I want the for loop to wait for the product, then begin the loop. How can I go about doing this?

The for loop just gets the product name, which is the same as the image name.

At the moment it doesn't add any images to add cache. But it does add the rest of the files.

Any help will be appreciated. Thanks in advance.

Calv
  • 125
  • 2
  • 11
  • Just so I understand, you want to wait for the `fetch` request to complete before you start your `for loop`? – goto Jan 25 '20 at 18:29
  • yes, i want my fetch request to complete before the for loop. so i can use the result of the fetch request in for loop. thanks – Calv Jan 25 '20 at 18:30
  • are you still having issues? – goto Jan 25 '20 at 21:27

5 Answers5

1

Because it's the function asynchronous fetch(), so you must put the code of treatment of response datas in function then()

var products = [];
fetch('http://localhost:3000/api/products')
    .then(res => res.json())
    .then(json => products = json)
    .then(() => {
       // ... treatement of product datas ...
       for(var i=0;i<products.length;i++){
         productImages.push('/PW1/img/'+products[i].name+'.jpg');
       }
     })
   .catch((e) => {
       console.log('Error', e);
    });

With the syntax ES6 JavaScript, I recommend you to use the sugar syntax "async-await" for the asynchronous code in JS. It helps you write the asynchronous code like synchronous code. And for sure, the synchronous code is much easier to understand. You should check the detail of my respone in another famous topic in StackOverFlow : How do I return the response from an asynchronous call?

SanjiMika
  • 2,664
  • 19
  • 19
1

fetch is asynchronous, so you need to create a callback that will run by calling then and do all the work that relies on the response that you get from calling your API:

// ...

var cacheName = "pwa-v1"
var toCache = ["/PW1/", "/PW1/Apps/calculate.js", ...]

fetch("http://localhost:3000/api/products")
  .then(res => res.json())
  .then(createFileCache)
  .catch(handleError)

function createFileCache(items) {
  var productImages = items.map(
    function(item) {
      return `/PW1/img/${item.name}.jpg`
    }
  )
  toCache.concat(productImages)
}

function handleError(err) {
  // handle errors appropriately
}

// ...

Keep in mind this is asynchronous, so if you need to rely on the new state of toCache somewhere else, then you might want to throw in another callback into the mix.

goto
  • 4,336
  • 15
  • 20
1

Use async/await. As a Promise it guarantees that either a response or rejection of requested data will be returned via the await keyword.

In the demo below the async function getPaths() will pass an endpoint (ie url of JSON) and an optional array (ie cached paths). It returns an array of all given paths. The endpoint is that of a live test server so you should have no problem testing it with your own endpoint. If you do...well you should seriously look into your back-end or Service Worker.

Demo

let toCache = [
  '/PW1/',
  '/PW1/Apps/calculate.js',
  '/PW1/Apps/login.js',
  '/PW1/Apps/index.js',
  '/PW1/index.html',
  '/PW1/products.html',
  '/PW1/style.css'
];

const getPaths = async(endpoint, cache = []) => {
  const response = await fetch(endpoint);
  let data = await response.json();

  data = cache.concat(data.map(product => `/PW1/img/${product.username}.jpg`));
  return data;
}

getPaths('https://jsonplaceholder.typicode.com/users', toCache).then(data => console.log(data));
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68
0

You need to put your for loop in a function as an argument to the .then method of the Promise fetch returns, just like your json conversion function

Something like:

var product = [];
var productImages = [];


fetch('http://localhost:3000/api/products')
.then(res => res.json())
.then(json => product = json)
.then(
   function() 
   {
         for(var i=0;i<product.length;i++)
         {
            productImages.push('/PW1/img/'+product[i].name+'.jpg');
         }         
   }
)
zer00ne
  • 41,936
  • 6
  • 41
  • 68
Freud Chicken
  • 525
  • 3
  • 8
0
const toCache= [
      '/PW1/',
      '/PW1/Apps/calculate.js',
      '/PW1/Apps/login.js',
      '/PW1/Apps/index.js',
      '/PW1/index.html',
      '/PW1/products.html',
      '/PW1/style.css',
];
const productImages = [];
fetch('http://localhost:3000/api/products')
    .then(res => res.json())
    .then(products => {
        for (const product of products) {
            productImages.push('/PW1/img/' + product.name + '.jpg');
        }
        let addCache = toCache.concat(productImages);
        // your logic can continue here
    });

Try not to use var, let is better and in your case const is even better since it's more restrictive

Simon
  • 11
  • 4