"Dividing" a task stream/executer

Consider the following action type:

interface SaveFoo {
  type: 'SAVE_FOO'
  payload: {
    id: string
    value: number
  }
}

I have a requirement to implement a saga that will apply throttling selectively. For instance, if the following actions are dispatched:

  1. { type: 'SAVE_FOO', payload: { id: "a", value: 1 } }
  2. { type: 'SAVE_FOO', payload: { id: "b", value: 1 } }
  3. { type: 'SAVE_FOO', payload: { id: "a", value: 2 } }
  4. { type: 'SAVE_FOO', payload: { id: "a", value: 3 } }

The goal is to initiate handlers for actions 1 and 2 as they have different id values, while actions 3 and 4 should be queued until action 1 is complete.

This seems like a common scenario but I couldn't find an existing solution. I attempted to implement it manually, but I believe there should be a more efficient approach:

  export function splitThrottle<T>(actionCreator: ActionCreator<T>, saga: (action: Action<T>) => SagaIterator, selector: (payload: T) => string) {
    const tasks: Record<string, Task> = {}
    const bufferLookup: Record<string, Buffer<Action<T>>> = {}

    function* queue(action: Action<T>, id: string) {
      try {
        yield call(saga, action)
      } catch (e) {
        // error handling
      }

      const next = bufferLookup[id].take()
      if (next) {
        tasks[id] = yield call(queue, next, id)
      } else {
        delete tasks[id]
      }
    }

    return function* () {
      while (true) {
        const action: Action<T> = yield take(actionCreator)
        const id = selector(action.payload)

        const existingTask = tasks[id]
        if (existingTask) {
          bufferLookup[id].put(action)
        } else {
          let buffer = bufferLookup[id]
          if (!buffer) {
            buffer = buffers.sliding(1)
            bufferLookup[id] = buffer
          }
          tasks[id] = yield fork(queue, action, id)
        }

      }
    }
  }

Answer №1

Below is my implementation which is similar to the original solution but with some variations:

export function* splitThrottle(pattern, saga, selector) {
  const channels = {}

  while (true) {
    const action = yield take(pattern)
    const id = selector(action)
    const { channel, justCreated } = obtainChannel(channels, id)

    yield put(channel, action)
    if (justCreated) {
      yield fork(processAllAndDelete, channels, id, saga)
    }
  }
}

function obtainChannel(channels, id) {
  let channel = channels[id]
  if (channel) {
    return { channel, justCreated: false }
  } else {
    channel = channels[id] = channel(buffers.expanding(1))
    return { channel, justCreated: true }
  }
}

function* processAllAndDelete(channels, id, saga) {
  const channel = channels[id]
  while (true) {
    const actions = yield flush(channel)
    if (!actions)
      break

    for (const action of actions) {
      yield call(saga, action)
    }
  }
  delete channels[id]
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Employing monaco-editor alongside typescript without webpack within an electron endeavor

I need help incorporating the monaco-editor into my electron project built with TypeScript. Using npm install -D monaco-editor, I installed it successfully and imported it with import { editor } from "monaco-editor";. Despite not receiving any mo ...

Can a number value in a JSON object be converted to a string?

In my application, I have defined an interface: export interface Channel { canal: string; name: number; status: string; temperature: number; setpoint: number; permission: boolean; percentOut: number; } [UPDATE] in the HTML file: <input ...

Retrieve information for the designated page exclusively

When retrieving data from the backend using a service, I encounter an issue where the system may slow down if 2000 records are returned in one request. To address this, I would like to display only 10 records per page and fetch the next 10 records with eac ...

Developing a hidden entity that adopts an interface with multiple functions that have been overloaded

My TypeScript interface includes a single function named "send" with two different allowed signatures. export interface ConnectionContext { send(data: ConnectionData): void; send(data: ConnectionData, timeout: number): Promise<ConnectionData> ...

The circular reference error in Typescript occurs when a class extends a value that is either undefined, not a constructor,

Let me begin by sharing some context: I am currently employed at a company where I have taken over the codebase. To better understand the issue, I decided to replicate it in a new nestjs project, which involves 4 entities (for demonstration purposes only). ...

Unit testing Firebase function with Jest for mocking

Currently, I am in the process of developing unit tests and facing challenges with mocking Firebase functions while specifying the return type upon calling them. The code snippet below illustrates what I intend to mock (account.service.ts) and provides ins ...

Error encountered during TypeScript compilation: Module 'fs' not found

I encountered an issue: TSError: ⨯ Unable to compile TypeScript: server/src/test/test.ts(2,45): error TS2307: Cannot find module 'fs' Every time I execute this particular test import "mocha" import { writeFileSync, readFileSync } from &a ...

Strategies for resolving the TypeScript 'possibly null' issue in ternary situations

Looking to enhance my code with a ternary operator for adding a class: className={clsx(classes.text, { classes.textSmall]: children.length > 11, })} Although this approach works, a TypeScript error is triggered: Object is possibly 'null' ...

I am struggling to comprehend the concept of dependency injection. Is there anyone available to provide a clear explanation for me?

I am working on a NestJS application and trying to integrate a task scheduler. One of the tasks involves updating data in the database using a UserService as shown below: import { Injectable, Inject, UnprocessableEntityException, HttpStatus, } fro ...

Definition files (.d.ts) for JavaScript modules

I'm currently working on creating Typescript typings for the link2aws package in order to incorporate it into my Angular project. Despite generating a .d.ts file, I am still encountering the following error message: TypeError: (new link2aws__WEBPACK_I ...

Could you provide insight into the reason behind debounce being used for this specific binding?

function debounce(fn, delay) { var timer return function () { var context = this var args = arguments clearTimeout(timer) timer = setTimeout(function () { fn.apply(context, args) }, delay) ...

The ngFor directive in Angular should be used with the Filter pipe to ensure that

Having a Filter implemented in my Angular Project that fetches data from Firebase. The current status in the Filter is as follows: Name 1: Lea Muster Name 2: Bruno Mustermann Name 3: Lea Muster Name 4: Gabriela Musterfrau The goal is to show duplicate e ...

Is there a way to trigger the click event in the week view of an Angular 2+ calendar?

https://i.sstatic.net/Vx2x8.png HTML Templates <mwl-calendar-week-view [viewDate]="viewDate" [refresh]="refresh" (click)="weekDayClick($event)"> </mwl-calendar-week-view> In the component file weekDayCl ...

Error encountered while building with Next.js using TypeScript: SyntaxError - Unexpected token 'export' in data export

For access to the code, click here => https://codesandbox.io/s/sweet-mcclintock-dhczx?file=/pages/index.js The initial issue arises when attempting to use @iconify-icons/cryptocurrency with next.js and typescript (specifically in typescript). SyntaxErr ...

Challenges encountered while compiling Node.js code with ts-node (Error: Cannot use import statement outside a module)

Trying to compile TypeScript code with NodeJS using this command: npx ts-node src/server.ts An error is thrown: SyntaxError: Cannot use import statement outside a module Following the error's instructions: Warning: To load an ES module, set " ...

How can you run a function in JavaScript or TypeScript that is stored as a string?

Is there a way to call a function that is stored as a string? For example: var dynamicFun = `function Hello(person) { return 'Hello' + person; }` In this case, the dynamicFun variable can store any function definition dynamically, such as: var ...

Encountering a Typescript issue when trying to access props.classes in conjunction with material-ui, react-router-dom

I could really use some help with integrating material-ui's theming with react-router-dom in a Typescript environment. I'm running into an issue where when trying to access classes.root in the render() method, I keep getting a TypeError saying &a ...

Tips for monitoring/faking method invocations within an Angular 5 service's constructor

My service involves making 2 method calls in the constructor: constructor(private http: HttpClient) { this.apiURL = environment.apiURL; this.method(); this.method2().subscribe(); } I am facing difficulties testing this service in the Test ...

Discovering the ASP.NET Core HTTP response header in an Angular application using an HTTP interceptor

I attempted to create a straightforward app version check system by sending the current server version to the client in the HTTP header. If there's a newer version available, it should trigger a notification for the user to reload the application. Ini ...

Tips for testing a mapbox popup using jasmine testing?

I encountered an issue with my mapbox popup while using jasmine and attempting to write a unit test for it. Here is the function in question: selectCluster(event: MouseEvent, feature: any) { event.stopPropagation(); this.selectedCluster = {geo ...