├── next-env.d.ts
├── public
├── favicon.ico
└── vercel.svg
├── README.md
├── src
├── WebWorker.ts
├── HttpService.ts
├── pages
│ └── index.tsx
├── ComponentWithWebWorker.tsx
└── useFetchWithWebWorker.ts
├── next.config.js
├── .gitignore
├── package.json
└── tsconfig.json
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s1doka/http-requests-with-webworkers/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## About
2 |
3 | Basic example on how to use an **WebWorker + NextJS + TypeScript**.
4 |
5 | Fetching data on a new thread created by the WebWorker with setInterval.
--------------------------------------------------------------------------------
/src/WebWorker.ts:
--------------------------------------------------------------------------------
1 | import {expose} from 'comlink'
2 | import {get} from './HttpService'
3 |
4 | const exports = {
5 | get
6 | }
7 |
8 | export type WebWorker = typeof exports
9 |
10 | expose(exports)
11 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | const WorkerPlugin = require('worker-plugin')
2 |
3 | module.exports = {
4 | webpack: (config, { isServer }) => {
5 | if (!isServer) {
6 | config.plugins.push(
7 | new WorkerPlugin({
8 | globalObject: 'self',
9 | })
10 | )
11 | }
12 | return config
13 | },
14 | }
--------------------------------------------------------------------------------
/src/HttpService.ts:
--------------------------------------------------------------------------------
1 | export async function get(url: string) {
2 | try {
3 | const response = await fetch(url)
4 |
5 | // Simulate a delay
6 | await new Promise(resolve => setTimeout(resolve, 5000));
7 |
8 | const json = await response.json()
9 |
10 | console.log(json)
11 |
12 | return json
13 | } catch (error) {
14 | console.log({error})
15 | }
16 | }
--------------------------------------------------------------------------------
/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import dynamic from "next/dynamic";
3 |
4 | const ComponentWithWebWorker = dynamic(() => import('../ComponentWithWebWorker'), {ssr: false})
5 |
6 | const Home: React.FunctionComponent = () => {
7 | return (
8 | <>
9 |
HTTP request with WebWorker
10 |
11 | >
12 | )
13 | }
14 |
15 | export default Home
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | .idea
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "http-requests-with-webworkers",
3 | "version": "0.1.0",
4 | "scripts": {
5 | "dev": "next dev",
6 | "build": "next build",
7 | "start": "next start"
8 | },
9 | "dependencies": {
10 | "comlink": "^4.3.0",
11 | "next": "9.4.4",
12 | "react": "16.13.1",
13 | "react-dom": "16.13.1",
14 | "worker-plugin": "^4.0.3"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^14.0.23",
18 | "@types/react": "^16.9.43",
19 | "typescript": "^3.9.6"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/ComponentWithWebWorker.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {useFetchWithWebWorker} from "./useFetchWithWebWorker";
3 |
4 | const ComponentWithWebWorker: React.FunctionComponent = () => {
5 | const {data, status} = useFetchWithWebWorker('https://archive.org/metadata/principleofrelat00eins')
6 |
7 | return (
8 | <>
9 | {status}
10 |
11 |
12 | {JSON.stringify(data, null, 2)}
13 |
14 |
15 | >
16 | )
17 | }
18 |
19 | export default ComponentWithWebWorker
20 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "webworker",
6 | "dom",
7 | "dom.iterable",
8 | "esnext"
9 | ],
10 | "allowJs": true,
11 | "skipLibCheck": true,
12 | "strict": true,
13 | "forceConsistentCasingInFileNames": true,
14 | "noEmit": true,
15 | "esModuleInterop": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "jsx": "preserve"
21 | },
22 | "exclude": [
23 | "node_modules"
24 | ],
25 | "include": [
26 | "next-env.d.ts",
27 | "**/*.ts",
28 | "**/*.tsx"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/useFetchWithWebWorker.ts:
--------------------------------------------------------------------------------
1 | import {wrap, releaseProxy} from "comlink"
2 | import {useEffect, useState, useMemo} from "react"
3 |
4 | type State = {
5 | status: 'LOADING' | 'IDLE' | 'ERROR'
6 | data: object
7 | }
8 |
9 | export function useFetchWithWebWorker(url: string) {
10 | const [state, setState] = useState({
11 | status: "LOADING",
12 | data: {}
13 | });
14 |
15 | const {webWorker} = useWorker('http-worker')
16 |
17 | useEffect(() => {
18 | const fetch = async () => {
19 | try {
20 | setState(prevState => ({status: "LOADING", data: prevState.data}));
21 |
22 | const data = await webWorker.get(url)
23 |
24 | setState({status: "IDLE", data})
25 | } catch (e) {
26 | setState(prevState => ({status: "IDLE", data: prevState.data}));
27 | }
28 | }
29 |
30 | setInterval(() => {
31 | fetch()
32 | }, 10000)
33 |
34 | }, [webWorker, setState, url]);
35 |
36 | return {
37 | data: state.data,
38 | status: state.status
39 | };
40 | }
41 |
42 | function useWorker(workerName: string) {
43 | const webWorkerAndCleanup = useMemo(() => createWebWorkerAndCleanup(workerName), []);
44 |
45 | useEffect(() => {
46 | const {cleanup} = webWorkerAndCleanup;
47 |
48 | return () => {
49 | cleanup();
50 | };
51 | }, [webWorkerAndCleanup]);
52 |
53 | return webWorkerAndCleanup;
54 | }
55 |
56 | /*
57 | * We can't use a parameter for the file path of the web worker because
58 | * TypeScript needs to be able to resolve the import at compile time.
59 | * */
60 | function createWebWorkerAndCleanup(workerName: string) {
61 | const worker = new Worker('./WebWorker', {
62 | name: workerName,
63 | type: "module"
64 | });
65 | const webWorker = wrap(worker);
66 |
67 | const cleanup = () => {
68 | webWorker[releaseProxy]();
69 | worker.terminate();
70 | };
71 |
72 | const webWorkerAndCleanup = {webWorker, cleanup};
73 |
74 | return webWorkerAndCleanup;
75 | }
--------------------------------------------------------------------------------