Skip to content

Instantly share code, notes, and snippets.

@prof3ssorSt3v3
Last active February 18, 2023 03:11
Show Gist options
  • Save prof3ssorSt3v3/0edce7080feb7b8b57165f3c6fe01300 to your computer and use it in GitHub Desktop.
Save prof3ssorSt3v3/0edce7080feb7b8b57165f3c6fe01300 to your computer and use it in GitHub Desktop.
Cache reference markdown page

Cache API

How Do I???

So, for all these examples the assumed set up is a main.js file that imports a cache.js file. All the methods dealing with the cache(s) will be inside the cache.js file and will return a Promise. Here is an example of a function inside the main.js file that calls a function in cache.js and receives a Promise in return. The code in main.js uses a then() to wait for the completion of the code in cache.js.

//main.js
import CACHE from './cache.js';
// from inside a method in main.js we will call a method in cache.js
const APP = {
  cacheRef: null, //a variable to hold a reference to a cache.
  someFunc(){
    CACHE.open('my-cache-name')
      .then(cache=>{
        APP.cacheRef = cache; //save the reference in the variable
      });
  }

If you need to do multiple things in a row, like open a cache, then delete a file, then get a list of all the files, then build the HTML for that list, then you need to use a chain of .then() methods with a return of the Promise method inside each then().

const APP = {
  someFunc(filename) {
    CACHE.open('my-cache-name')
      .then((cache) => {
        APP.cacheRef = cache; //save the reference to the cache
        return CACHE.delete(filename);
      })
      .then(() => {
        //now the delete is complete,
        //get the list from the cache passed in
        return CACHE.getFiles(APP.cacheRef);
      })
      .then((fileList) => {
        //fileList is the array of files
        //build the HTML
        return APP.buildList(fileList);
      })
      .then(() => {
        //HTML is built... need to update anything else?
      })
      .catch((err) => {
        //handle an error from any step
      });
  },
};

Open a Cache and keep a Reference

If all we need to do is open a cache and keep a reference to that cache for later use...

const CACHE = {
  open(cacheName) {
    return caches.open(cacheName);
  },
};

Get a List of Caches

If you want a list of all the available caches...

const CACHE = {
  listCaches() {
    return caches.keys();
  },
};

Then back in the main JS file we get an array that we can loop through.

//main.js
CACHE.listCaches().then((list) => {
  //list is an array that you can loop through
});

Delete Old Caches

If you need to delete one or more caches we can use Promise.all() to delete an array of names. This is a less common thing to do. Typically, we do this in a Service Worker when we want to get rid of old versions of caches.

We pass the name (or names) of cache(s) to keep to our function. It will return a Promise that lets us know when it is complete.

const CACHE = {
  deleteCache(toKeep) {
    return CACHE.listCaches().then((keys) => {
      //keys is the array of all the cache names
      return Promise.all(keys.filter((key) => key != toKeep).map((name) => name.delete()));
    });
  },
};

Get a List of Files in a Cache

This function could be written in two ways. First, you can pass a reference to the cache that contains the files. Second, you could pass the name of the cache that holds the files

const CACHE = {
  getFileList(cache) {
    //pass in the cache where the files are located
    //version 1 - by cache reference
    return cache.keys();
  },
  getFileList(cacheName) {
    //if you pass in the name of the cache instead, then you would call CACHE.open() first
    //version 2 - by cache name
    return CACHE.open(cacheName).then((cache) => {
      return cache.keys();
    });
  },
};

Back in the main JS file we get an array of all the Request objects from the cache. Each of those Request objects has a url property which can be used to create a URL object. From the URL object you can extract any part of the url that you want - origin, hostname, pathname, port, protocol, query string, hash, etc.

//main.js
const APP = {
  cacheRef: null,
  init() {
    CACHE.open('my-cache-name').then((cache) => {
      APP.cacheRef = cache;
    });
  },
  getFiles() {
    CACHE.getFileList(APP.cacheRef).then((fileList) => {
      //fileList is an array of each Request in the Cache
      fileList.forEach((req) => {
        let url = new URL(req.url);
        console.log(url.pathname);
        console.log(url.origin);
        console.log(url.search);
        console.log(url.port);
        console.log(url.hash);
      });
    });
  },
};

Delete a File in a Cache

To delete a file from a cache you need to have the file name or a Request containing the file name, plus a way to reference the cache. To reference the cache you need a cache name or a variable holding the reference to the cache. For this example we will pass in the reference to the cache.

You could alternatively pass in a Request object or URL object instead of a filename.

const CACHE = {
  deleteFile(filename, cache) {
    return cache.delete(filename);
  },
};

Add a File to a Cache

Your function to add a file to a cache could accept a filename, a URL, or a Request object. The best approach is to have a Request object. This way you can define things like the HTTP method, whether it is cors or no-cors request, and whether or not credentials should be included.

The file to be saved needs to be wrapped in a Response object that also gets passed to the CACHE function.

//in main.js
let filename = crypto.randomUUID() + '.json';
let request = new Request(filename, {
  method: 'get',
  credentials: omit,
});
let data = JSON.stringify({ id: 123, some: 'info', type: 'object' });
let file = new File([data], filename, { type: 'application/json' });
let response = new Response(file, { status: 200, statusText: 'all good' });
CACHE.saveFile(APP.cacheRef, request, response)
  .then(() => {
    //this function runs after the file is saved.
  })
  .catch((err) => {
    //handles failure to save file in cache.
  });

The third thing that gets passed to the function is a reference to the cache.

//in cache.js
const CACHE = {
  saveFile(cache, req, resp) {
    return cache.put(req, resp);
  },
};

Get the Contents from any File

When a cache saves files, it does so with key value pairs. The Request object is the key and the Response object is the value.

The Response object is a container that holds a File. Depending on the type of file there is a different response method that we have to use to get the contents.

Whether it is a Response object coming from a Cache or a Response object coming from a fetch, it is still just a Response object.

In our cache function we need a filename but we can uses caches instead of a reference to a single cache.

In the main Javascript file we will be given a Promise containing the contents of a file OR an Error.

//main.js
let filename = 'style.css';
CACHE.getText(filename)
  .then((txt) => {
    //txt is the text from the css file
  })
  .catch((err) => {
    //Error about no match
  });

Get the Contents from a Text File

To get the contents of a text file, which includes .txt, .html, or .css, we use the .text() method.

const CACHE = {
  getText(filename) {
    return caches.match(filename).then((cacheResponse) => {
      //cacheResponse is the Response object or null
      if (cacheResponse) return cacheResponse.text();
      throw new Error('No match for text file in the cache');
    });
  },
};

Get the Contents from a JSON File

To get an Object built from the contents of a .json text file we would use the .json() method on the Response object that is returned by the match() method.

const CACHE = {
  getJSON(filename) {
    return caches.match(filename).then((cacheResponse) => {
      //cacheResponse is the Response object or null
      if (cacheResponse) return cacheResponse.json();
      throw new Error('No match for json file in the cache');
    });
  },
};

Get the Contents from an Image File

When you want an image (or any media file) from a cache we will use the blob() method.

The result from the blob() method is actual just a reference to the location in memory where the binary data from inside the image file is saved.

const CACHE = {
  getImage(filename) {
    return caches.match(filename).then((cacheResponse) => {
      //cacheResponse is the Response object or null
      if (cacheResponse) return cacheResponse.blob();
      throw new Error('No match for media file in the cache');
    });
  },
};

Then back in the main JavaScript file when we receive the blob reference variable, we need to use the URL.createObjectURL() method which will create a URL that points to the location in memory. This newly created URL can be used as the src for any <img> element.

//main.js
let filename = 'myavatar.png';

CACHE.getImage(filename)
  .then((blobRef) => {
    //blobRef is the pointer to the location in memory of the blob
    let url = URL.createObjectURL(blobRef);
    let img = document.getElementById('someImage');
    img.src = url;
  })
  .catch((err) => {
    //error about no file match in cache
  });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment