Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

reuse-promise

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

reuse-promise - npm Package Compare versions

Comparing version 1.0.1 to 1.1.0

4

CHANGELOG.md
# Change Log
## [1.1.0] - 2016-07-06
- Added `serializeArguments` option [#1](https://github.com/elado/reuse-promise/pull/1) ([@Tyler-Murphy](https://github.com/Tyler-Murphy))
## [1.0.0] - 2016-04-18
- Initial release

161

lib/index.js

@@ -1,2 +0,2 @@

'use strict';
"use strict";

@@ -7,11 +7,15 @@ Object.defineProperty(exports, "__esModule", {

var _promise = require('babel-runtime/core-js/promise');
var _keys = require("babel-runtime/core-js/object/keys");
var _keys2 = _interopRequireDefault(_keys);
var _promise = require("babel-runtime/core-js/promise");
var _promise2 = _interopRequireDefault(_promise);
var _assign = require('babel-runtime/core-js/object/assign');
var _extends2 = require("babel-runtime/helpers/extends");
var _assign2 = _interopRequireDefault(_assign);
var _extends3 = _interopRequireDefault(_extends2);
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify = require("babel-runtime/core-js/json/stringify");

@@ -26,99 +30,20 @@ var _stringify2 = _interopRequireDefault(_stringify);

// TODO compare key with shallow equal, not JSON
// TODO equality check function
var _allPromiseMapsByArgs = [];
var _allMemoizedValueMapsByArgs = [];
var pendingPromisesMap = function () {
var FN_INDEX = 0;
var KEY_INDEX = 1;
var VALUE_INDEX = 2;
function serializeArguments(key) {
return (0, _stringify2.default)(key);
}
var a = [];
function serializeKey(key) {
return (0, _stringify2.default)(key);
}
function is(item, fn, serializedKey) {
if (item[FN_INDEX] == fn && item[KEY_INDEX] == serializedKey) {
return true;
}
return false;
}
function getIndex(fn, serializedKey) {
for (var i = 0, l = a.length; i < l; i++) {
var item = a[i];
if (is(item, fn, serializedKey)) {
return i;
}
}
return -1;
}
function get(fn, key) {
var serializedKey = serializeKey(key);
var i = getIndex(fn, serializedKey);
if (a[i]) {
return a[i][VALUE_INDEX];
}
return null;
}
function set(fn, key, value) {
var serializedKey = serializeKey(key);
var i = getIndex(fn, serializedKey);
if (i > -1) {
a[i][VALUE_INDEX] = value;
} else {
var obj = [];
obj[FN_INDEX] = fn;
obj[KEY_INDEX] = serializedKey;
obj[VALUE_INDEX] = value;
a.push(obj);
}
return value;
}
function del(fn, key) {
if (key === undefined) {
// delete all of function
for (var i = a.length - 1; i >= 0; i--) {
var item = a[i];
if (item[FN_INDEX] == fn) {
a.splice(i, 1);
}
}
} else {
// delete single entry by function and key
var serializedKey = serializeKey(key);
var _i = getIndex(fn, serializedKey);
if (_i > -1) {
a.splice(_i, 1);
}
}
}
function clear() {
a.length = 0;
}
return {
get: get,
set: set,
del: del,
clear: clear
};
}();
function reusePromise(origFn) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
options = (0, _assign2.default)({ memoize: false }, options);
options = (0, _extends3.default)({
memoize: false,
serializeArguments: serializeArguments
}, options);
var promiseMapsByArgs = {};
var memoizedValuesByArgs = {};
var wrappedFn = function wrappedFn() {

@@ -129,21 +54,12 @@ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {

var key = args;
var memoizeValueKey = ['memoized', key];
var key = options.serializeArguments(args);
var pendingPromise = pendingPromisesMap.get(origFn, key);
var pendingPromise = promiseMapsByArgs[key];
if (pendingPromise) {
return pendingPromise;
}
if (pendingPromise) return pendingPromise;
if (options.memoize) {
var prevValue = pendingPromisesMap.get(origFn, memoizeValueKey);
if (options.memoize && key in memoizedValuesByArgs) return _promise2.default.resolve(memoizedValuesByArgs[key]);
if (prevValue) {
return _promise2.default.resolve(prevValue);
}
}
var forgetPromise = function forgetPromise() {
pendingPromisesMap.del(origFn, key);
return delete promiseMapsByArgs[key];
};

@@ -154,6 +70,3 @@

var promise = origPromise.then(function (value) {
if (options.memoize) {
pendingPromisesMap.set(origFn, memoizeValueKey, value);
}
if (options.memoize) memoizedValuesByArgs[key] = value;
forgetPromise();

@@ -165,3 +78,3 @@ return value;

});
pendingPromisesMap.set(origFn, key, promise);
promiseMapsByArgs[key] = promise;

@@ -173,4 +86,8 @@ return promise;

wrappedFn.__reusePromise__clear = function () {
reusePromise.clear(origFn);
reusePromise.clear(wrappedFn);
};
wrappedFn.__reusePromise__promiseMapsByArgs = promiseMapsByArgs;
wrappedFn.__reusePromise__memoizedValuesByArgs = memoizedValuesByArgs;
_allPromiseMapsByArgs.push(promiseMapsByArgs);
_allMemoizedValueMapsByArgs.push(memoizedValuesByArgs);

@@ -180,2 +97,8 @@ return wrappedFn;

function clearObject(object) {
(0, _keys2.default)(object).forEach(function (k) {
return delete object[k];
});
}
function clear() {

@@ -185,5 +108,7 @@ var fn = arguments.length <= 0 || arguments[0] === undefined ? undefined : arguments[0];

if (fn === undefined) {
pendingPromisesMap.clear();
_allPromiseMapsByArgs.forEach(clearObject);
_allMemoizedValueMapsByArgs.forEach(clearObject);
} else {
pendingPromisesMap.del(fn.__reusePromise__origFn || fn);
clearObject(fn.__reusePromise__promiseMapsByArgs);
clearObject(fn.__reusePromise__memoizedValuesByArgs);
}

@@ -190,0 +115,0 @@ }

{
"name": "reuse-promise",
"version": "1.0.1",
"version": "1.1.0",
"description": "Reuse the same promise until resolved when retrieving it from a function",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -7,4 +7,6 @@ # reuse-promise

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 be executed.
> **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:

@@ -14,7 +16,5 @@

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

@@ -30,8 +30,6 @@

`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.
`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.
Promises are kept in cache and returned without recreating a new one only while they are in progress. When the promise is resolved, it'll be cleared from this temporary cache, allowing a new call to `findArticle(1)` to recreate a promise and issue an HTTP 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])`.
Promises are kept in an index by the arguments that were sent to the function, so `findArticle(1)` and `findArticles([1, 2, 3])` will go through the original function and create a new promise, and any following call with the same arguments will reuse the same promise. The comparison between two sets of arguments is by `JSON.stringify` the argument array, hence a call with a new array [1, 2, 3] will still reuse the same promise.
## Installation

@@ -47,3 +45,3 @@

### As a class decoartor
### As a class decorator

@@ -58,7 +56,3 @@ Requires `babel` and `babel-plugin-transform-decorators-legacy` plugin.

find(articleId) {
return new Promise((resolve, reject) => {
fetch(`/article/${articleId}`).then(r => r.json()).then(function (data) {
resolve(data)
})
})
return fetch(`/article/${articleId}`).then(r => r.json())
}

@@ -83,7 +77,3 @@ }

function findArticle(articleId) {
return new Promise((resolve, reject) => {
fetch(`/article/${articleId}`).then(r => r.json()).then(function (data) {
resolve(data)
})
})
return fetch(`/article/${articleId}`).then(r => r.json())
}

@@ -112,7 +102,3 @@

find(articleId) {
return new Promise((resolve, reject) => {
fetch(`/article/${articleId}`).then(r => r.json()).then(function (data) {
resolve(data)
})
})
return fetch(`/article/${articleId}`).then(r => r.json())
}

@@ -149,2 +135,26 @@ }

### option: `serializeArguments`
By default, `reuse-promise` indexes promises in a dictionarty where the key is all arguments `JSON.stringify`ied. 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:
```js
@reusePromise({
serializeArguments: args => args[0][0]
})
```
Or, to grab an ID of a given model without having it all serialized:
```js
updateUserName = reusePromise(updateUserName, {
serializeArguments: args => args[0].id
})
const someUser = { id: 1, name: 'name' }
updateUserName(someUser, 'new name')
```
## Test

@@ -151,0 +161,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc