npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

controlled-actions

v0.0.4

Published

Controleld actions gives a simple interface to control your javascript async routines.

Downloads

11

Readme

Controlled actions

Build Status npm

Controlled actions is a pack of helper classes that help you to control async routines that can be called multiple times. It is a wrapper over the Promise API.

Why

When studying the flux spec, I started to implement it. I've imagined "actions" as simple async functions that performs some async routine and distribute side effects (such as application state changes).

In this scenario, Actions could be executed by everything that can dispatch an async routine, like API Calls, for instance.

The first issues appeared when we had multiple components that called the same endpoint with the same params. Those components could dispatch Action executions at the same time. In order to prevent multiple backend calls with the same payload, I've created something like ActionFirst. As time passed, new types of concurrency treatment appeared, then has born controlled-actions.

Since it relies only on the JS Promises API, it can be used anywhere it is supported. Node or the browser. Using React or any other UI Library.

In some of my projects, I use it alongside mobx in a fancy redux-less flux implemmentation.

Alternatives

As anything else related to software development, controlled-actions is not suitable for every project out there. Here are some aletrnativeas that may be more suitable for your project:

  • If you are using redux on your project, you can easily reproduce this concurrent handling behavior with redux-saga.
  • RxJS can be a good alternative too, but be careful to not implement a overkill. Read more about it here.

Installing

npm i -S controlled-actions

or

yarn add controlled-actions

Basic usage example

In this example, FetchImages is an abstraction of an API call through an axios endpoint and an update on a local store.

// FetchImages.js
import { ActionFirst } from "controlled-actions"
import localStore from "./localStore"
import { imageEndpoint } from "./endpoints"

const FetchImages = new ActionFirst(
  async () => {
    const images = await imageEndpoint.get()
    localStore.updateImages(images) // this can be a store, like a reducer or a mobx-observable's action
    return images
  }
)

// Somewhere_else.js
...
async componentDidMount(){
 const images = await FetchImages.execute()
 //by this line, local store is updated
}
Without async/await

If you dont want to use async/await, you can use controlled-actions by simply returning an Promise or a value. Afterall, the return will be evalued as a Promise.resolve(returnedValue) See the above example:

// FetchImages.js
import { ActionFirst } from "controlled-actions"
import localStore from "./localStore"
import { imageEndpoint } from "./endpoints"

const FetchImages = new ActionFirst(
  () => {
    return new Promise((resolve, reject)=>{
      const images = await imageEndpoint.get()
      localStore.updateImages(images)
      resolve(images)
    })
  }
)

How to use it

Controlled actions includes the following classes:

Action, ActionFirst, ActionForceFirst and ActionLast

Each one of those, deals with concurrent calls in a different way. Let's see how they work:

Action

Starting with the simplest: Action. Action is wrapper around a simple asynchronous routine, here is a example:

import { Action } from "controlled-actions"

const BringMePie = new Action(async ()=>{
  const pie = await fetchPie() //some api call to an pie endpoint, for example
  return pie
})

And somewhere else:

const myPie = await BringMePie.execute();
// Number of times fetchPie has been called: 1

You can pass as a parameter, a payload or in this case, a flavor:

import { Action } from "controlled-actions"

const BringMePie = new Action(
  async (flavor)=>{
    const pie = await fetchPie(flavor) //some api call to an pie endpoint, for example
    return pie
  }
)

const myPie = await BringMePie.execute("apple pie");
// Number of times fetchPie has been called: 1
// myPie is a "apple pie"

"I can do that just with JS Promises". Action alone can be considered pretty useless, but if you consider the ones who extend it, it can be a good way to standardize the application when you need special runtime features, as provided by ActionFirst, ActionForceFirst and ActionLast

ActionFirst

If you execute an ActionFirst at teh same time another execution, with an equal payload that has not been resolved yet, it will hang on the ongoing Promise.

import { ActionFirst } from "controlled-actions"

const BringMePie = new ActionFirst(
  async (flavor)=>{
    const pie = await fetchPie(flavor) //some api call to an pie endpoint, for example
    return pie
  }
)

const myPiePromise = BringMePie.execute("apple pie");
const myPiePromise2 = BringMePie.execute("apple pie"); //Called before myPiePromise resolves

const myPie = await myPiePromise
const myPie2 = await myPiePromise2
// Number of times fetchPie has been called: 1
// Both myPie and myPie2 is the same "apple pie"

Pay attention to how many times fetchPie has been called.

If it is called with different payloads, it will execute concurrently:

const myPiePromise = BringMePie.execute("apple pie");
const myPiePromise2 = BringMePie.execute("lime pie"); // Called before myPiePromise resolves, 
                                                      // but with different payloads

const myPie = await myPiePromise
const myPie2 = await myPiePromise2
// Number of times fetchPie has been called: 2
// myPie is a "apple pie"
// myPie1 is a "lime pie"

ActionForceFirst

ActionForceFirst works just like ActionFirst. Except it does not evaluates equal payloads.

import { ActionForceFirst } from "controlled-actions"

const BringMePie = new ActionForceFirst(
  async (flavor)=>{
    const pie = await fetchPie(flavor)  //some api call to an pie endpoint, for example
    return pie
  }
)

const myPiePromise = BringMePie.execute("apple pie");
const myPiePromise2 = BringMePie.execute("lime pie"); // Called before myPiePromise resolves, 
                                                      // but with different payloads
const myPie = await myPiePromise
const myPie2 = await myPiePromise2
// Number of times fetchPie has been called: 1
// Both myPie and myPie2 is the same "apple pie"

ActionLast

As the name suggests, ActionLast 's executions always resolves to the last call's response.

import { ActionLast } from "controlled-actions"

const BringMePie = new ActionLast(
  async (flavor)=>{
    const pie = await fetchPie(flavor)
    return pie
  }
)

const myPiePromise = BringMePie.execute("apple pie");
const myPiePromise2 = BringMePie.execute("lime pie"); //Called before myPiePromise resolves
const myPiePromise3 = BringMePie.execute("avocado pie"); //Called before myPiePromise 1 and 2 resolves

const myPie = await myPiePromise
const myPie2 = await myPiePromise2
const myPie3 = await myPiePromise3
// Number of times fetchPie has been called: 3
// myPie, myPie2 and myPie3 is the same "avocado pie"

API Reference

Action

import { Action } from "controlled-actions"

Concurrent actions.

Constructor:
new Action<PayloadType, ResolveType>(actionRoutine)
actionRoutine:
(payload: PayloadType) => Promise<ResolveType>

A function that receives a payload; Performs some async stuff, and returns a promise. It is the routine performed by the action. It is recommended to use an async function.

Methods:
execute : PayloadType => Promise<ResolveType>

Starts an execution of the action.

ActionFirst

import { ActionFirst } from "controlled-actions"

Actions whose executions hangs on ongoing calls, if there is the same payload. Extends Action. Constructors and methods have the same signatures as Actions.

ActionForceFirst

import { ActionForceFirst } from "controlled-actions"

Actions whose executions hang on ongoing calls, despite payload. Extends ActionFirst.

Constructors and methods have the same signatures than the Actions's.

ActionLast

import { ActionLast } from "controlled-actions"

Actions whose executions will always resolve to the last call. Extends Action.

Constructors and methods have the same signatures than the Actions's.

Contributing

If you got so far reading this README, you are maybe thinking about contributing. Pull requests are welcome.