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

tstl-singleton

v1.0.0

Published

Asynchronous Singleton generator through TSTL

Downloads

9

Readme

TSTL-Singleton

GitHub license npm version Downloads Build Status

1. Outline

Asynchronous Singleton Generator using TSTL.

The tstl-singleton is an Asynchronous Singleton Generator, who guarantees the asynchronous lazy constructor to be called "only one at time". The "only one at time" would always be kepted, even in the race condition, through Mutex and UniqueLock who are imlemented in the TSTL.

2. Installation

Installing tstl-singleton in NodeJS is very easy. Just install with the npm.

npm install --save tstl-singleton

To use the tstl-singleton, import Singleton class and create the Singleton instance with your custom lazy constructor. After the creation, call the Singleton.get() method, then it would return the promised value with lazy construction.

import { Singleton } from "tstl-singleton";
import { Member } from "./Member";

export namespace RemoteAssets
{
    export function getMembers(): Promise<Member[]>
    {
        return members_.get();
    }
    
    const members_: Singleton<Member[]> = new Singleton(() =>
    {
        let response: Response = await fetch("https://some-domain.com/members", {
            method: "GET"
        });
        return await response.json();
    });
}

3. Usage

3.1. Basic Concepts

declare module "tstl-singleton"
{
    export class Singleton<T>
    {
        /**
         * Create singleton generator with the *lazy constructor*.
         */
        public constructor(getter: () => Promise<T>);
        
        /**
         * Get the *lazy constructed* value.
         */
        public get(): Promise<T>;

        /**
         * Reload value by calling the *lazy constructor* forcibly.
         */
        public reload(): Promise<T>;
    }
}

Using the tstl-singleton is also easy, too. Create a Singleton instance with your custom lazy constructor and get the promised value thorugh the Singleton.get() method. The Singleton.get() method would construct the return value following below logics:

  • At the first time: calls the lazy constructor and returns the value.
  • At the lazy construction: returns the pre-constructed value.
  • When race condition:
    • simultaneously call happens during the lazy construction.
    • guarantees the "only one at time" through a mutex.

If you want to reload the promised value, regardless of whether the lazy construction has been completed or not, call the Singleton.reload() method. It would call the lazy constructor forcibly, even if the lazy construction has been completed in sometime.

3.2. Demonstration

As I've mentioned, tstl-singleton always ensures the lazy constructor to be called "only one at time". As you can see from the below example code, even simultaneous call on the Singleton.get() has been occcured, the lazy constructor would be called "only one at time".

import { Singleton } from "tstl-singleton";
import { randint } from "tstl/algorithm/random";
import { sleep_for } from "tstl/thread/global";

async function main(): Promise<void>
{
    // GENERATE SINGLETON WITH LAZY-CONSTRUCTOR 
    let index: number = 0;
    let singleton: Singleton<number> = new Singleton(async () =>
    {
        await sleep_for(randint(50, 500));
        return ++index;
    });

    // RACE CONDITIO: SIMULTANEOUS ACCESS
    let promises: Promise<number>[] = [];
    for (let i: number = 0; i < 5; ++i)
        promises.push( singleton.get() );
    
    // OUTPUT: ALL ELEMENTS MUST BE 1
    console.log(await Promise.all(promises));

    // RELOAD: WOULD BE 2
    console.log(await singleton.reload());
}
1 1 1 1 1
2

3.3. Sample Case

Loading remote data from the external API would be a heavy work for the remote server. Therefore, it would better to call the external API, only when it requires. In such reason, loading remote data from the external API can be the best use case for the tstl-singleton.

With the lazy constructor, you can call the external API only when you need it. Also, you can avoid the vulnerable API callings by using the Singleton.get() method in the race condition, which helps you to call the external API "only one at time".

Look at the below code and feel what the "only one at time" means:

import { Singleton } from "tstl-singleton";

import { Company } from "./Company";
import { Member } from "./Member";

export namespace RemoteAssets
{
    export function getCompanies(): Promise<Company[]> 
    {
        return companies_.get();
    }
    export function getMembers(): Promise<Member[]>
    {
        return members_.get();
    }

    async function fetchItems<Entity>(path: string): Promise<Entity[]>
    {
        let url: string = `https://some-domain.com${path}`;
        let response: Response = await fetch(url, { method: "GET" });
        return await response.json();
    }
    const companies_: Singleton<Company[]> = new Singleton(() => fetchItems("/companies"));
    const members_: Singleton<Member[]> = new Singleton(() => fetchItems("/members"));
}