├── 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 | 3 | 4 | -------------------------------------------------------------------------------- /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 | } --------------------------------------------------------------------------------