DEV Community

artydev
artydev

Posted on • Edited on

Simple Concurrent Tasks in Javascript

Here is a very nice and tiny library from Samrith Shankar Concurent Tasks

And a demo : DemoTasks

function sleep (ms) {
  return new Promise((res, rej) => { 
    setTimeout(() => {
      res(ms)
    }, ms)
  })
}


const task = (i) => async (done) =>  { 
  done();   // automatically added by the library
  let r = await sleep(Math.random() * 500);       
  results.innerHTML += (`<div class="task">task${i} ended in ${r} ms</div>`);    
}

function createTasks (number = 3) {
  let tasks = []
   for (let i = 0; i < number; i++) {       
     tasks.push(task(i) )        
   }
  return tasks
} 

const runner = new TaskRunner() 

function start_tasks () {
  results.innerHTML = "";
  runner.addMultiple(createTasks()); 
}
Enter fullscreen mode Exit fullscreen mode

Enhanced version with AI:

h1("Concurrent Test (requestAnimationFrame)")

class LineCounter {
  constructor({ delay, label = "count", start = 0 }) {
    this.value = start
    this.label = label
    this.delay = delay

    this.lastTime = performance.now()
    this.el = p(this.format())
  }

  update(now) {
    if (now - this.lastTime >= this.delay) {
      this.value++
      this.el.textContent = this.format()
      this.lastTime = now
    }
  }

  format() {
    return `${this.label} = ${this.value}`
  }
}

// create counters
const counters = [
  new LineCounter({ delay: 100, label: "fast" }),
  new LineCounter({ delay: 650, label: "medium" }),
  new LineCounter({ delay: 1550, label: "slow" })
]

// single animation loop
function loop(now) {
  counters.forEach(counter => counter.update(now))
  requestAnimationFrame(loop)
}

requestAnimationFrame(loop)
Enter fullscreen mode Exit fullscreen mode

Fixed Time Step

h1("Concurrent Test (fixed timestep)")

class LineCounter {
  constructor({ step, label = "count", start = 0 }) {
    this.value = start
    this.label = label
    this.step = step

    this.accumulator = 0
    this.el = p(this.format())
  }

  tick() {
    this.value++
    this.el.textContent = this.format()
  }

  format() {
    return `${this.label} = ${this.value}`
  }
}

// counters with fixed steps (ms)
const counters = [
  new LineCounter({ step: 100, label: "fast" }),
  new LineCounter({ step: 650, label: "medium" }),
  new LineCounter({ step: 1550, label: "slow" })
]

let lastTime = performance.now()

function loop(now) {
  const delta = now - lastTime
  lastTime = now

  counters.forEach(counter => {
    counter.accumulator += delta

    while (counter.accumulator >= counter.step) {
      counter.tick()
      counter.accumulator -= counter.step
    }
  })

  requestAnimationFrame(loop)
}

requestAnimationFrame(loop)

Enter fullscreen mode Exit fullscreen mode

Top comments (7)

Collapse
 
efpage profile image
Eckehard

Hy,

mayby I did not get the point? This seemed already to be easy in JS...

h1("Concurrent-Test")

class line {

  constructor(t){
    this.val = 0
    this.p1=p("count = 0")
    setInterval(() => this.inc(),t)
  }

  inc(){
    this.p1.textContent = "count = "+(++this.val)
  }
}
new line(100)
new line(650)
new line(1550)
Enter fullscreen mode Exit fullscreen mode

see DEMO

Collapse
 
artydev profile image
artydev • Edited

Hy Eckehard,
I am building a web scrapper.
I need to query some Proxy wich limits concurents requests, that's why I ma interested in this library

Here is a demo of what I ma trying to do : WS

This is a draft, in real I use DML :)

Collapse
 
efpage profile image
Eckehard

Which kind of limitation do we face here? I know, that Javascript is single threaded, so we possibly could block the execution. But IΒ΄m not really aware about the differences in the approach you describe...

Thread Thread
 
artydev profile image
artydev • Edited

In fact the limitations is not in Javascript.
The problem is to be able to make a limited concurents requests / s
For example, my host don't permit mor than 5 request/s, and I have many requests to do.
So I have to find a way to put all requests in queue and manage the calling...

Collapse
 
artydev profile image
artydev • Edited

Betwise, this is a neat tip, I am trying to use for my use case... :-)

Collapse
 
efpage profile image
Eckehard

Using a fixed request rate might limit your performance. Maybe itΒ΄s better to limit the number of open request allow the next request only, if the number of open requests is lower than a certain amount. This cares to keep enough requests open, but not flooding your API. You should also care for requests that are never answered, so there should be some kind of timeout to delete unanswered requests after some time.

This kind of event handling is best done with objects, that care for their own state. You can use a simple array as a stack, new request objects are just pushed ontop of this stack. Objects that are finished should be able to remove themselves from the stack. Everytime, a request is finished, it can trigger the next waiting object on the stack.

Thread Thread
 
artydev profile image
artydev

Thank you Eckehard, great advices