Skip to content

गेम लॉजिक के लिए Web Workers

Web Workers आपको JavaScript को एक background thread में चलाने देते हैं। गेम्स के लिए इसका मतलब है कि physics, AI और procedural generation बिना frames गिराए चल सकते हैं।

1) Workers का इस्तेमाल कब करें

अच्छे उम्मीदवार:

  • Physics simulation
  • Pathfinding (A*, navmesh)
  • AI decision making
  • Procedural generation
  • Asset processing (image manipulation, compression)
  • जटिल math (FFT, collision detection)

जिनके लिए सही नहीं:

  • Rendering (Workers सीधे DOM/Canvas access नहीं कर सकते)
  • बहुत छोटे tasks (thread का overhead इसके लायक नहीं होता)

2) एक बेसिक Worker बनाना

worker.js:

js
self.onmessage = (e) => {
  const { type, data } = e.data
  
  if (type === 'calculate') {
    const result = heavyCalculation(data)
    self.postMessage({ type: 'result', data: result })
  }
}

function heavyCalculation(input) {
  // Expensive work here
  return input * 2
}

main.js:

js
const worker = new Worker('worker.js')

worker.onmessage = (e) => {
  const { type, data } = e.data
  if (type === 'result') {
    console.log('Got result:', data)
  }
}

worker.postMessage({ type: 'calculate', data: 42 })

3) Inline Workers (कोई अलग फाइल नहीं)

js
function createInlineWorker(fn) {
  const blob = new Blob([`(${fn.toString()})()`], { type: 'text/javascript' })
  return new Worker(URL.createObjectURL(blob))
}

const worker = createInlineWorker(() => {
  self.onmessage = (e) => {
    const result = e.data * 2
    self.postMessage(result)
  }
})

4) ES module Workers

आप worker code को एक ES module की तरह लिख सकते हैं और उसके अंदर importScripts के बजाय import का इस्तेमाल कर सकते हैं। constructor को { type: 'module' } पास करें:

js
const worker = new Worker('physics-worker.js', { type: 'module' })

worker के अंदर आप फिर शेयर्ड math, collision या AI helpers को उसी तरह import कर सकते हैं जैसे main thread पर करते हैं, ताकि गेम लॉजिक दोहराया न जाए। Module workers Chrome और Edge 80+, Safari 15+ और Firefox 114+ में सपोर्ट होते हैं।

5) Physics Worker का उदाहरण

physics-worker.js:

js
const bodies = []
const FIXED_DT = 1 / 60

self.onmessage = (e) => {
  const { type, data } = e.data
  
  switch (type) {
    case 'init':
      initWorld(data)
      break
    case 'step':
      step()
      break
    case 'addBody':
      bodies.push(data)
      break
  }
}

function initWorld(config) {
  // Initialize physics world
}

function step() {
  // Update physics
  for (const body of bodies) {
    body.vy += 9.8 * FIXED_DT // Gravity
    body.x += body.vx * FIXED_DT
    body.y += body.vy * FIXED_DT
  }
  
  // Send positions back
  self.postMessage({
    type: 'positions',
    data: bodies.map(b => ({ id: b.id, x: b.x, y: b.y, rotation: b.rotation }))
  })
}

6) परफॉर्मेंस के लिए Transferable objects

बड़े data को बिना copy किए transfer किया जा सकता है:

js
// Main thread
const positions = new Float32Array(1000)
worker.postMessage(positions, [positions.buffer])
// positions is now unusable here (transferred)

// Worker
self.onmessage = (e) => {
  const positions = e.data
  // Work with positions
  self.postMessage(positions, [positions.buffer])
}

7) Pathfinding Worker

pathfinding-worker.js:

js
let grid = null

self.onmessage = (e) => {
  const { type, data } = e.data
  
  if (type === 'setGrid') {
    grid = data
  }
  
  if (type === 'findPath') {
    const path = aStar(data.start, data.end, grid)
    self.postMessage({ type: 'path', id: data.id, path })
  }
}

function aStar(start, end, grid) {
  // A* implementation
  const openSet = [start]
  const cameFrom = new Map()
  const gScore = new Map()
  gScore.set(key(start), 0)
  
  while (openSet.length > 0) {
    // ... A* logic
  }
  
  return reconstructPath(cameFrom, end)
}

function key(pos) {
  return `${pos.x},${pos.y}`
}

8) Parallel tasks के लिए Worker pool

js
class WorkerPool {
  constructor(workerUrl, size = navigator.hardwareConcurrency || 4) {
    this.workers = []
    this.queue = []
    this.available = []
    
    for (let i = 0; i < size; i++) {
      const worker = new Worker(workerUrl)
      worker.onmessage = (e) => this.handleResult(worker, e)
      this.workers.push(worker)
      this.available.push(worker)
    }
  }
  
  run(data) {
    return new Promise((resolve) => {
      const task = { data, resolve }
      
      if (this.available.length > 0) {
        this.dispatch(this.available.pop(), task)
      } else {
        this.queue.push(task)
      }
    })
  }
  
  dispatch(worker, task) {
    worker._currentTask = task
    worker.postMessage(task.data)
  }
  
  handleResult(worker, e) {
    const task = worker._currentTask
    task.resolve(e.data)
    
    if (this.queue.length > 0) {
      this.dispatch(worker, this.queue.shift())
    } else {
      this.available.push(worker)
    }
  }
  
  terminate() {
    this.workers.forEach(w => w.terminate())
  }
}

9) रियल-टाइम sync के लिए SharedArrayBuffer

cross-origin isolation चालू होने पर, आप memory शेयर कर सकते हैं:

js
// Main thread
const shared = new SharedArrayBuffer(1024)
const positions = new Float32Array(shared)

worker.postMessage({ type: 'init', buffer: shared })

// Worker reads/writes directly to shared memory
// No postMessage overhead for position updates

10) Workers के लिए OffscreenCanvas

एक Worker में render करें। OffscreenCanvas अब Chrome, Edge, Firefox और Safari में Baseline रूप से व्यापक रूप से उपलब्ध है (Safari ने macOS और iOS पर 17.0 में सपोर्ट जोड़ा):

js
// Main thread
const canvas = document.getElementById('game')
const offscreen = canvas.transferControlToOffscreen()
worker.postMessage({ canvas: offscreen }, [offscreen])

// Worker
self.onmessage = (e) => {
  const canvas = e.data.canvas
  const ctx = canvas.getContext('2d')
  
  function render() {
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    // Draw...
    requestAnimationFrame(render)
  }
  render()
}

11) Error handling

js
worker.onerror = (e) => {
  console.error('Worker error:', e.message, e.filename, e.lineno)
}

// In worker
self.onerror = (e) => {
  self.postMessage({ type: 'error', message: e.message })
}

संबंधित

बाहरी संसाधन