• Stars
    star
    103
  • Rank 333,046 (Top 7 %)
  • Language
    JavaScript
  • Created over 8 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Reuse the same promise that's returned from a function until it's resolved

reuse-promise

build status npm version codeclimate

Purpose

TL;DR - Prevent from a unique async process (function that returns a promise) to run more than once concurrently by temporarily caching the promise until it's resolved/rejected.

When a function returns a promise and it's being called from multiple places in the app, new promises are being instantiated, and multiple async operations are going to be executed.

A common case is a function that gets an articleId and returns a promise that calls API. This function can be called from multiple places, each time will create a new promise and will issue a new request. This is usually not desired:

function findArticle(articleId) {
  return fetch(`/article/${articleId}`).then(r => r.json())
  // could also be
  // return new Promise(...)
}

// will issue first request for articleId=1
findArticle(1).then(article1 => console.log(article1))
// will issue second request for articleId=1
findArticle(1).then(article1 => console.log(article1))
// will issue first request for articleId=2
findArticle(2).then(article2 => console.log(article2))

reuse-promise decorates a function and temporary memoizes a promise until it's resolved. In this case, the first call for articleId=1 will create the new promise, issue the HTTP request, and remember that created promise for articleId=1. The second call with the same argument will return the same promise from earlier call. However, once the original promise is resolved (or rejected), a new call to findArticle(1) will issue a new request.

An initial call to a wrapped function goes through the original function, and then indexes the returned promise by a json-serialized string of the arguments that were sent to the function. So findArticles([1, 2, 3]) can be called twice and still return the same promise, becasue JSON.stringify([1, 2, 3]) === JSON.stringify([1, 2, 3]).

Installation

npm install reuse-promise --save

Usage

reuse-promise can be used as a decorator in a class definition or as a wrapper to a function.

As a class decorator

Requires babel and babel-plugin-transform-decorators-legacy plugin.

import { decorator as reusePromise } from 'reuse-promise'

class ArticleService {
  @reusePromise()
  find(articleId) {
    return fetch(`/article/${articleId}`).then(r => r.json())
  }
}

const articleService = new ArticleService()

// will issue first request for articleId=1
articleService.find(1).then(article1 => console.log(article1))
// WILL NOT issue any request for articleId=1, will reuse the promise that was created in previous call
articleService.find(1).then(article1 => console.log(article1))
// will issue first request for articleId=2
articleService.find(2).then(article2 => console.log(article2))

Wrapping a function

import reusePromise from 'reuse-promise'

function findArticle(articleId) {
  return fetch(`/article/${articleId}`).then(r => r.json())
}

const findArticleReusedPromise = reusePromise(findArticle/*, options */)


// will issue first request for articleId=1
findArticleReusedPromise(1).then(article1 => console.log(article1))
// WILL NOT issue any request for articleId=1, will reuse the promise that was created in previous call
findArticleReusedPromise(1).then(article1 => console.log(article1))
// will issue first request for articleId=2
findArticleReusedPromise(2).then(article2 => console.log(article2))

option: memoize

reuse-promise can indefinitely remember the value that was returned from a promise, so no async code will execute more than once, even if the promise was previously resolved:

import { decorator as reusePromise } from 'reuse-promise'

class ArticleService {
  @reusePromise({ memoize: true })
  find(articleId) {
    return fetch(`/article/${articleId}`).then(r => r.json())
  }
}

const articleService = new ArticleService()

articleService.find(1).then(article1 => console.log(article1))

setTimeout(() => {
  // here, the original promise is resolved
  // without memoize: true, calling find(1) would go through original function and create a promise
  // however, with memoize the following call will be immediately resolved with the value

  articleService.find(1).then(article1 => console.log(article1))
}, 1000)

Clearing all memoized values of a function can be done with:

reusePromise.clear(articleService.find)

// or
articleService.find.__reusePromise__clear()

Clear all:

reusePromise.clear()

option: serializeArguments

By default, reuse-promise indexes promises in a dictionarty where the key is all arguments JSON.stringifyied. This is sometimes an unnecessary process, especially when sending big objects as arguments.

A custom argument serializer can be provided. To reuse promises based on the first letter of the first argument, for example, provide:

@reusePromise({
  serializeArguments: args => args[0][0]
})

Or, to grab an ID of a given model without having it all serialized:

updateUserName = reusePromise(updateUserName, {
  serializeArguments: args => args[0].id
})

const someUser = { id: 1, name: 'name' }

updateUserName(someUser, 'new name')

Test

npm install
npm test

License

MIT

More Repositories

1

jastor

Auto translates NSDictionary to instances of Objective-C classes, supporting nested types and arrays
Objective-C
344
star
2

next-with-less

Next.js + Less CSS Support
JavaScript
144
star
3

isotope

Ruby Hybrid Template Engine for Client Side and Server Side, EJS-Based
Ruby
55
star
4

lodash-bound

Enables chained lodash functions with ES bind (::) syntax
JavaScript
39
star
5

class-private-method-decorator

Private methods in a JavaScript ES6 class using an ES7 decorator
JavaScript
30
star
6

rollup-plugin-preserve-shebangs

A Rollup plugin that preserves shebangs (#!/usr/bin/env node) in output files
TypeScript
17
star
7

hoist-non-react-methods

Copies non-react specific methods from a child component to a parent component
JavaScript
11
star
8

kaching

Makes your DB suffer less from COUNT(*) queries and check-for-existence queries of associations (has_many and has_many :through), by keeping and maintaining counts and lists on Redis, for faster access.
Ruby
7
star
9

sidekiq_custom_serializer

An extension for Sidekiq that brings custom serialization of arguments such as ActiveRecord instances, classes, modules and custom objects.
Ruby
3
star
10

mootools-trackinstances

Track Class Instances With a Class Mutator
JavaScript
2
star
11

fake-https-cert

JavaScript
1
star
12

react-ref-method-forwarder

Allows accessing methods of HOC-wrapped components through normal React refs
JavaScript
1
star
13

barcode

JavaScript
1
star
14

mootools-laziness

Class Mutator to reduce memory usage of class' prototype objects and function pointers unless an instance is instantiated
JavaScript
1
star