ndrean / asyn-promise

Fetch sequential, parallel, batch

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Introduction

Demo of different fetch constructions client-side. We just render the ids in the browser to view the result of testing some fetch chaining strategies. We used a public API (endpoint: "https://reqres.in/api/users/" supporting CORS and https or "https://jsonplaceholder.typicode.com/users/"). The data is saved in cache. A link to a live demo using surge. < https://ndrean.github.io/asyn-promise/.

Live demo app at:

https://fetch-cache-post.surge.sh/

Sources:

fetch and cache API

Back to start

  • async await with try/catch.
  • The cache API is also implemented with await.

Parallel fetching

Back to start

Given an array usersIds = [1,...n], we map to an array of promises indexed by usersIds. These promises are simply given by fetch(uri/{id}) and rendered in the browser. We then call Promise.all([arrayOfPromises]).

Sequential fetching

Back to start

Suppose we have a fixed number of tasks/promises. If we simply do promise1.then(promise2).then(promise3), we can't capture the return values. Then we can do (display is just a browser rendering method):

Promise.resolve()
  .then((r) => {
    return promise1.then((r) =>  display(...));
})
  .then((r) => {
    return promise2.then((r) =>  display(...));
})
  .then((r) => {
    return promise3.then((r) =>  display(...));
});

https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/reduce

If we have dynamic promises, we put all the promises in an array and use reduce() to collapse the array of promises into a single promise chain.

The reduce() method executes a provided callback function callback which takes the previousValue and currentValue which iterates over the array. The function stores the result in the accumulator which will be the previousValue. It uses and initial value. Here, we take Promise.resolve([]), a promise that always resolves to an empty array as the initial value. Then accumulator === initialValue on the first time through the callback.

We will capture the result and push it to the array, further used as arrayOfResults. It is

then used to display results in the browser with our displaymethod.

promises
  .reduce((promiseChain, currentPromise) => {
    return promiseChain.then((result) => {
    return currentPromise.then((currentResult) => {return [...result, currentResult}]);
  // we return an array that is used as "arrayOfResults"
  });
}, Promise.resolve([])) // the initial value
  // we use the result array (simply indexes) to render in the browser
  .then((arrayOfResults) => {
    console.log(arrayOfResults);
  arrayOfResults.forEach((result) => {
    console.log(result);
  display("#resu8", result, "Seq :");
  });
});

Batch fetching

Back to start

We use the same Promise.all but this time we slice the array of 'usersId' into smaller arrays; we then map to a promise on each subarrays to produce subarrays of promises. Finaly, we run promise.all([]).

cache

Back to start

We can use the cache to store http GET (only GET). To do so, we just declare a cache by giving it a name and add a stringified key/value {request: reponse} (we just need to declare request in case of an http fetch).

const request = new Request(url);
const response = await fetch(request);
// create/open a new named cache
const newCache = await caches.open("cacheName");
// add the {request:response}
await newCache.add(request);
// await cache.put(request, response) if not from web

To review it, we can inspect Application/cache in Chrome or display it in the console with the snippet below:

// we look for the request in the cache
const responseFromCache = await caches.match(request);
// then we parse it
const matchedCachedObj = await responseFromCache.json();
// and review in the console:
console.log("cacheName", matchedCachedObj.data.email);

AXIOS

Back to start

Implementation of alternative library Axios: looping and post.

Workbox

TODO

Bundling browserify

Back to start

If we want to work with indexedDB, we can use the npm package idb. To do so, we have a working file index.js that contains the code using idb. To use it, we need to require the module idb, so we need to bundle it. We can use a quick tool such as the npm package browserify:

Browserify lets you require('modules') in the browser by bundling up all of your dependencies.

browserify will bundle index.js inside a new file bundle.js , that will launched by the browser with a script in index.html. We then can use the idb module with a require('idb') inside index.js.

yarn add idb browserify

browserify index.js -o bundle.js

Bundling Webpack

Back to start

Install npm packages

We initialize yarn (the flag -y will skip all the questions) with yarn init (-y) and then install webpack with all needed dependencies (--dev or -Dto install locally):

yarn add webpack webpack-cli webpack-dev-server copy-webpack-plugin html-webpack-plugin css-loader style-loader -D

Since we used Axios, instead of adding the following cdn script in the index.html file:

script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js" /script

we import the package with yarn add axios and add the import where needed in our .js files:

+ import axios from "axios";

Directories setup

Our files system should look like

|-.cache (if Parcel)
|-node_modules
|-.gitignore
|-readme.md
|-package.json
|-yarn.lock
|-/dist
|-|-index.html
|-|-main.js
|-/scr
|-|-/img
|-|-|-icon-512x512.png
|-|-|-icon-192x192.png
|-|-|-...
|-|-index.html
|-|-index.js
|-|-functions.js
|-|-forms.js
|-|-styles.css
|-|-...

Gitignore

We add .cache node_modules dist in the .gitignore file.

Exclude browsers that does not accept ES5

#package.json
	"browserslist": [ "since  2017-06" ]

webpack.config.js

Here is a simple Webpack configuration file:

# webpack.config.js
const  webpack  =  require("webpack");
const  path  =  require("path");
const  HtmlWebpackPlugin  =  require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {

  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"), // our distribution directory will be: `/dist
    filename: "main.js", // the name of the final JS file
  },
  devServer: {
      contentBase: "./src/", //absolute path recommendedpath.resolve(__dirname, "src"),
      watchContentBase: true, // full page reload, for the static html change
    },
  devtool: "inline-source-map", // helper to locate errors
  plugins: [
  // clean folder after production building
  new  CleanWebpackPlugin(),
  //will automatically inject bundle js into ./dist/index.html (output folder)
  new  HtmlWebpackPlugin({
    template: "src/index.html",
    filename: "index.html",
  }),
  //copy assets not explicitely referenced from our Javascript
  new  CopyWebpackPlugin([
    { from: "src/img", to: "img/" }
    ]),
  ],
  module: { // to compile the CSS files
    rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }],
  },
};

Note on CSS

We added the packages css-loader (for the import) and style-loader (to inject as style in pages) packages firstly (declared in package.json).

We have defined how to import CSS files in webpack.config.js, namely how to find them and them compile and inject the styles:

module: {rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }]}

  • We add import "./styles.css" within index.js
#index.js
+ import "./styles.css"

We added bootstrap which needs the packages jquery and popper.js and relies on css-loader since we imported Compiled CSS.

#index.js
import "bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
  • we remove (because of style-loader) the link in the header of the index.html file:

script link rel="stylesheet" type="text/css" href="./styles.css" script

Hot reload

We configured:

devServer: {
  contentBase: "./src/", //absolute path recommendedpath.resolve(__dirname, "src"),
  watchContentBase: true, // full page reload, for the static html change
},

and use with webpack --mode development --hot --watch compilation mode.

npm scripts in package.json

Webpack is configured within the file webpack.config.js. With the CLI, we can run commands like yarn webpack --mode development --config webpack.config.js to compile in development mode for example. Webpack will use the default configuration file webpack.config.js of webpack if present.

the webpack.config.js file is picked by default if present, so we can not mention it

Instead of running commands for webpack-cli in the terminal, we can add helpers by adding npm scripts to the package.json file.

# package.json
{
"scripts":{
	"clean": "rm dist/*",
	"dev": " webpack --mode development --hot --watch (--config webpack.config.js)",
	"build": "webpack -p --mode production",
	"serve": "webpack-dev-server"
	}
}

We can now run in a terminal on of the following commands:

yarn clean (this empties the '/dist' folder)
yarn dev (bundles in development mode)
yarn build (bundles in production mode when ready to minimize and output in the '/dist' folder)
yarn serve (runs a development server)

Remove Link to index.js

We will compile the project to the main.js file in the /dist folder: this is declared in webpack.config.js with (output: [...,filename: "main.js"])).

script type="module" scr="index.js"> /script

Compile and launch web-pack-dev-server

To compile the project, we run in the terminal the npm scripts helpers that we defined: yarn dev or yarn build once it's finished.

Once the compilation is made, with this config, we will serve the files with webpack-dev-server so that the --watch mode is automatically on, meaning that it will recompile automatically whenever files change (HTML or CSS or Javascript) so that we don't have to reload the page or stop/start the web server.

Notes for Parcel.js

  • Firstly, we need a file index.html and create a directory /src and put all our files inside. The main js file will be named /src/index.js. The link to this file should be declared in the index.html file without type="module" (which is needed for webpackotherwise).

script type="module" src="src/index.js"> /script

  • Then since we use async/await, we can limit the accepted of browsers to those who accept ES5, through the file package.json ( if we use the bundler Parcel)
#package.json
	"browserslist": [ "since  2017-06" ]

Error handling

Back to start

https://stackoverflow.com/questions/45285129/any-difference-between-await-promise-all-and-multiple-await/54291660#54291660

About

Fetch sequential, parallel, batch


Languages

Language:JavaScript 75.5%Language:HTML 22.5%Language:CSS 2.0%