Comparing version 3.2.1 to 3.3.0
@@ -30,3 +30,13 @@ declare type Unsubscribe = () => void; | ||
} | ||
interface VoidSubscription { | ||
interface ISubscription<T> { | ||
subscribe: SubscribeMethod<T>; | ||
unsubscribe: UnsubscribeMethod<T>; | ||
unsubscribeAll: UnsubscribeAllMethod; | ||
isSubscribed: IsSubscribedMethod<T>; | ||
size: () => number; | ||
emit: (newValue: T) => void; | ||
destroy: () => void; | ||
isDestroyed: () => boolean; | ||
} | ||
interface IVoidSubscription { | ||
subscribe: VoidSubscribeMethod; | ||
@@ -38,19 +48,29 @@ unsubscribe: VoidUnsubscribeMethod; | ||
emit: () => void; | ||
destroy: () => void; | ||
isDestroyed: () => boolean; | ||
} | ||
interface SubscriptionOptions { | ||
/** | ||
* @deprecated use IVoidSubscription instead | ||
*/ | ||
declare type VoidSubscription = IVoidSubscription; | ||
interface ISubscriptionOptions { | ||
onFirstSubscription?: () => void; | ||
onLastUnsubscribe?: () => void; | ||
onDestroy?: () => void; | ||
maxSubscriptionCount?: number; | ||
maxRecursiveCall?: number; | ||
} | ||
interface Subscription<T> { | ||
subscribe: SubscribeMethod<T>; | ||
unsubscribe: UnsubscribeMethod<T>; | ||
unsubscribeAll: UnsubscribeAllMethod; | ||
isSubscribed: IsSubscribedMethod<T>; | ||
size: () => number; | ||
emit: (newValue: T) => void; | ||
} | ||
declare function Subscription<T = void>(options?: SubscriptionOptions): [T] extends [void] ? VoidSubscription : Subscription<T>; | ||
/** | ||
* @deprecated use ISubscriptionOptions instead | ||
*/ | ||
declare type SubscriptionOptions = ISubscriptionOptions; | ||
/** | ||
* @deprecated use ISubscription instead | ||
*/ | ||
declare type Subscription<T> = ISubscription<T>; | ||
declare const Subscription: (<T = void>(options?: ISubscriptionOptions) => [T] extends [void] ? IVoidSubscription : ISubscription<T>) & { | ||
create: <T_1>(options?: ISubscriptionOptions) => ISubscription<T_1>; | ||
createVoid: (options?: ISubscriptionOptions) => IVoidSubscription; | ||
}; | ||
export { IsSubscribedMethod, OnUnsubscribed, SubscribeMethod, Subscription, SubscriptionCallback, SubscriptionOptions, Unsubscribe, UnsubscribeAllMethod, UnsubscribeMethod, VoidIsSubscribedMethod, VoidSubscribeMethod, VoidSubscription, VoidSubscriptionCallback, VoidUnsubscribeMethod }; | ||
export { ISubscription, ISubscriptionOptions, IVoidSubscription, IsSubscribedMethod, OnUnsubscribed, SubscribeMethod, Subscription, SubscriptionCallback, SubscriptionOptions, Unsubscribe, UnsubscribeAllMethod, UnsubscribeMethod, VoidIsSubscribedMethod, VoidSubscribeMethod, VoidSubscription, VoidSubscriptionCallback, VoidUnsubscribeMethod }; |
252
dist/mod.js
@@ -25,128 +25,160 @@ var __defProp = Object.defineProperty; | ||
module.exports = __toCommonJS(mod_exports); | ||
function Subscription(options = {}) { | ||
const { | ||
onFirstSubscription, | ||
onLastUnsubscribe, | ||
maxRecursiveCall = 1e3, | ||
maxSubscriptionCount = 1e4 | ||
} = options; | ||
const subscriptions = []; | ||
let nextSubscriptions = []; | ||
const emitQueue = []; | ||
let isEmitting = false; | ||
function emit(newValue) { | ||
emitQueue.push({ value: newValue }); | ||
if (isEmitting) { | ||
return; | ||
var Subscription = (() => { | ||
return Object.assign(legacyCreate, { create, createVoid }); | ||
function legacyCreate(options = {}) { | ||
return create(options); | ||
} | ||
function createVoid(options = {}) { | ||
return create(options); | ||
} | ||
function create(options = {}) { | ||
const { | ||
onFirstSubscription, | ||
onLastUnsubscribe, | ||
onDestroy, | ||
maxRecursiveCall = 1e3, | ||
maxSubscriptionCount = 1e4 | ||
} = options; | ||
const subscriptions = []; | ||
let nextSubscriptions = []; | ||
const emitQueue = []; | ||
let isEmitting = false; | ||
let destroyed = false; | ||
const sub = { | ||
subscribe, | ||
unsubscribe, | ||
unsubscribeAll, | ||
isSubscribed, | ||
emit, | ||
size, | ||
destroy, | ||
isDestroyed | ||
}; | ||
return sub; | ||
function isDestroyed() { | ||
return destroyed; | ||
} | ||
isEmitting = true; | ||
let emitQueueSafe = maxRecursiveCall + 1; | ||
while (emitQueueSafe > 0 && emitQueue.length > 0) { | ||
emitQueueSafe--; | ||
const value = emitQueue.shift().value; | ||
nextSubscriptions = [...subscriptions]; | ||
let safe = maxSubscriptionCount; | ||
while (safe > 0 && nextSubscriptions.length > 0) { | ||
safe--; | ||
const item = nextSubscriptions.shift(); | ||
item.callback(value); | ||
function destroy() { | ||
if (destroyed) { | ||
return; | ||
} | ||
if (safe <= 0) { | ||
destroyed = true; | ||
unsubscribeAll(); | ||
if (onDestroy) { | ||
onDestroy(); | ||
} | ||
} | ||
function emit(newValue) { | ||
if (destroyed) { | ||
throw new Error("The subscription has been destroyed"); | ||
} | ||
emitQueue.push({ value: newValue }); | ||
if (isEmitting) { | ||
return; | ||
} | ||
isEmitting = true; | ||
let emitQueueSafe = maxRecursiveCall + 1; | ||
while (emitQueueSafe > 0 && emitQueue.length > 0) { | ||
emitQueueSafe--; | ||
const value = emitQueue.shift().value; | ||
nextSubscriptions = [...subscriptions]; | ||
let safe = maxSubscriptionCount; | ||
while (safe > 0 && nextSubscriptions.length > 0) { | ||
safe--; | ||
const item = nextSubscriptions.shift(); | ||
item.callback(value); | ||
} | ||
if (safe <= 0) { | ||
isEmitting = false; | ||
throw new Error("The maxSubscriptionCount has been reached. If this is expected you can use the maxSubscriptionCount option to raise the limit"); | ||
} | ||
} | ||
if (emitQueueSafe <= 0) { | ||
isEmitting = false; | ||
throw new Error("The maxSubscriptionCount has been reached. If this is expected you can use the maxSubscriptionCount option to raise the limit"); | ||
throw new Error("The maxRecursiveCall has been reached, did you emit() in a callback ? If this is expected you can use the maxRecursiveCall option to raise the limit"); | ||
} | ||
} | ||
if (emitQueueSafe <= 0) { | ||
isEmitting = false; | ||
throw new Error("The maxRecursiveCall has been reached, did you emit() in a callback ? If this is expected you can use the maxRecursiveCall option to raise the limit"); | ||
} | ||
isEmitting = false; | ||
} | ||
function subscribe(arg1, arg2, arg3) { | ||
const subId = typeof arg1 === "string" ? arg1 : null; | ||
const callback = typeof arg1 === "string" ? arg2 : arg1; | ||
const onUnsubscribe = typeof arg1 === "string" ? arg3 : arg2; | ||
if (typeof callback !== "function") { | ||
throw new Error("Expected the callback to be a function."); | ||
} | ||
const alreadySubscribed = findSubscription(subId, callback); | ||
if (alreadySubscribed) { | ||
if (subId !== null) { | ||
alreadySubscribed.callback = callback; | ||
function subscribe(arg1, arg2, arg3) { | ||
if (destroyed) { | ||
throw new Error("The subscription has been destroyed"); | ||
} | ||
alreadySubscribed.onUnsubscribe = onUnsubscribe; | ||
const subIndex = subscriptions.indexOf(alreadySubscribed); | ||
subscriptions.splice(subIndex, 1); | ||
subscriptions.push(alreadySubscribed); | ||
return alreadySubscribed.unsubscribe; | ||
} | ||
let isSubscribed2 = true; | ||
subscriptions.push({ | ||
subId, | ||
callback, | ||
unsubscribe: unsubscribeCurrent, | ||
onUnsubscribe | ||
}); | ||
if (subscriptions.length === 1 && onFirstSubscription) { | ||
onFirstSubscription(); | ||
} | ||
function unsubscribeCurrent() { | ||
if (!isSubscribed2) { | ||
return; | ||
const subId = typeof arg1 === "string" ? arg1 : null; | ||
const callback = typeof arg1 === "string" ? arg2 : arg1; | ||
const onUnsubscribe = typeof arg1 === "string" ? arg3 : arg2; | ||
if (typeof callback !== "function") { | ||
throw new Error("Expected the callback to be a function."); | ||
} | ||
isSubscribed2 = false; | ||
const index = subscriptions.findIndex((i) => i.callback === callback); | ||
if (index === -1) { | ||
console.warn(`Subscribe (isSubscribed === true) callback is not in the subscriptions list. Please report a bug.`); | ||
return; | ||
const alreadySubscribed = findSubscription(subId, callback); | ||
if (alreadySubscribed) { | ||
if (subId !== null) { | ||
alreadySubscribed.callback = callback; | ||
} | ||
alreadySubscribed.onUnsubscribe = onUnsubscribe; | ||
const subIndex = subscriptions.indexOf(alreadySubscribed); | ||
subscriptions.splice(subIndex, 1); | ||
subscriptions.push(alreadySubscribed); | ||
return alreadySubscribed.unsubscribe; | ||
} | ||
const item = subscriptions[index]; | ||
subscriptions.splice(index, 1); | ||
const queueIndex = nextSubscriptions.findIndex((i) => i.callback === callback); | ||
if (queueIndex >= 0) { | ||
nextSubscriptions.splice(queueIndex, 1); | ||
let isSubscribed2 = true; | ||
subscriptions.push({ | ||
subId, | ||
callback, | ||
unsubscribe: unsubscribeCurrent, | ||
onUnsubscribe | ||
}); | ||
if (subscriptions.length === 1 && onFirstSubscription) { | ||
onFirstSubscription(); | ||
} | ||
if (item.onUnsubscribe) { | ||
item.onUnsubscribe(); | ||
function unsubscribeCurrent() { | ||
if (!isSubscribed2) { | ||
return; | ||
} | ||
isSubscribed2 = false; | ||
const index = subscriptions.findIndex((i) => i.callback === callback); | ||
if (index === -1) { | ||
console.warn(`Subscribe (isSubscribed === true) callback is not in the subscriptions list. Please report a bug.`); | ||
return; | ||
} | ||
const item = subscriptions[index]; | ||
subscriptions.splice(index, 1); | ||
const queueIndex = nextSubscriptions.findIndex((i) => i.callback === callback); | ||
if (queueIndex >= 0) { | ||
nextSubscriptions.splice(queueIndex, 1); | ||
} | ||
if (item.onUnsubscribe) { | ||
item.onUnsubscribe(); | ||
} | ||
if (subscriptions.length === 0 && onLastUnsubscribe) { | ||
onLastUnsubscribe(); | ||
} | ||
} | ||
if (subscriptions.length === 0 && onLastUnsubscribe) { | ||
onLastUnsubscribe(); | ||
return unsubscribeCurrent; | ||
} | ||
function findSubscription(subId, callback) { | ||
return subId === null ? subscriptions.find((l) => l.callback === callback) : subscriptions.find((l) => l.subId === subId); | ||
} | ||
function unsubscribeAll() { | ||
while (subscriptions.length > 0) { | ||
subscriptions[0].unsubscribe(); | ||
} | ||
return; | ||
} | ||
return unsubscribeCurrent; | ||
} | ||
function findSubscription(subId, callback) { | ||
return subId === null ? subscriptions.find((l) => l.callback === callback) : subscriptions.find((l) => l.subId === subId); | ||
} | ||
function unsubscribeAll() { | ||
while (subscriptions.length > 0) { | ||
subscriptions[0].unsubscribe(); | ||
function unsubscribe(arg1) { | ||
const [subId, callback] = typeof arg1 === "string" ? [arg1, void 0] : [null, arg1]; | ||
const subscription = findSubscription(subId, callback); | ||
if (subscription) { | ||
subscription.unsubscribe(); | ||
} | ||
} | ||
return; | ||
} | ||
function unsubscribe(arg1) { | ||
const [subId, callback] = typeof arg1 === "string" ? [arg1, void 0] : [null, arg1]; | ||
const subscription = findSubscription(subId, callback); | ||
if (subscription) { | ||
subscription.unsubscribe(); | ||
function isSubscribed(arg1) { | ||
const [subId, callback] = typeof arg1 === "string" ? [arg1, void 0] : [null, arg1]; | ||
const subscription = findSubscription(subId, callback); | ||
return subscription !== void 0; | ||
} | ||
function size() { | ||
return subscriptions.length; | ||
} | ||
} | ||
function isSubscribed(arg1) { | ||
const [subId, callback] = typeof arg1 === "string" ? [arg1, void 0] : [null, arg1]; | ||
const subscription = findSubscription(subId, callback); | ||
return subscription !== void 0; | ||
} | ||
function size() { | ||
return subscriptions.length; | ||
} | ||
const sub = { | ||
subscribe, | ||
unsubscribe, | ||
unsubscribeAll, | ||
isSubscribed, | ||
emit, | ||
size | ||
}; | ||
return sub; | ||
} | ||
})(); | ||
// Annotate the CommonJS export names for ESM import in node: | ||
@@ -153,0 +185,0 @@ 0 && (module.exports = { |
{ | ||
"name": "suub", | ||
"version": "3.2.1", | ||
"version": "3.3.0", | ||
"description": "A simple pub/sub written in Typescript", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -14,3 +14,3 @@ <p align="center"> | ||
const mySub = Subscription<number>(); | ||
const mySub = Subscription.create<number>(); | ||
@@ -30,3 +30,3 @@ const unsub = mySub.subscribe((num) => { | ||
To create a `Subscription` you need to import the `Subscription` function and call it. | ||
To create a `Subscription` you need to import the `Subscription.create` function and call it. | ||
@@ -36,6 +36,6 @@ ```ts | ||
const subscription = Subscription(); | ||
const subscription = Subscription.create(); | ||
``` | ||
If you use TypeScript, you need to pass a type parameter to the `Subscription` function to define the type of the value associated with the subscription. By default this type is `void` (no value). | ||
If you use TypeScript, you need to pass a type parameter to the `Subscription.create` function to define the type of the value associated with the subscription. | ||
@@ -45,5 +45,13 @@ ```ts | ||
const numSubscription = Subscription<number>(); | ||
const numSubscription = Subscription.create<number>(); | ||
``` | ||
If you don't want your subscription not to emit any value, you can use the `Subscription.createVoid` function. | ||
```ts | ||
import { Subscription } from 'suub'; | ||
const voidSubscription = Subscription.createVoid(); | ||
``` | ||
### Subscribe and Unsubscribe | ||
@@ -129,8 +137,9 @@ | ||
The `Subscription` function accept an option object as parameter (all properties are optional): | ||
The `Subscription.create` (or `Subscription.createVoid`) function accept an option object as parameter (all properties are optional): | ||
```ts | ||
const sub = Subscription({ | ||
const sub = Subscription.create({ | ||
onFirstSubscription: () => {}, | ||
onLastUnsubscribe: () => {}, | ||
onDestroy: () => {}, | ||
maxSubscriptionCount: 10000, | ||
@@ -149,2 +158,6 @@ maxRecursiveCall: 1000, | ||
#### `onDestroy` | ||
> A function called when the `destroy` method is called. Note that during this call the `Subscription` is already destroyed and you can't call `emit` or `subscribe` anymore. | ||
#### `maxSubscriptionCount` | ||
@@ -175,2 +188,18 @@ | ||
### Destroying a Subscription | ||
You can call the `destroy` method to destroy a subscription. This will unsubscribe all callback and call the `onDestroy` option if any. | ||
```ts | ||
subscription.destroy(); | ||
``` | ||
Once destroyed, calling `emit` or `subscribe` will throw an error. You can still call the other methods but they will have no effect. | ||
You can check if a subscription is destroyed by calling the `isDestroyed` method. | ||
```ts | ||
subscription.isDestroyed(); // <- boolean | ||
``` | ||
## Some precisions | ||
@@ -196,2 +225,14 @@ | ||
#### Calling `destroy` will unsubscribe all callback and call the `onUnsubscribe` if any | ||
In these `onUnsubscribe` callback the subscription is considered destroyed so you can't call `emit` or `subscribe` anymore. | ||
#### Calling `destroy` on a destroyed subscription will have no effect | ||
This is a no-op, it will not call `onDestroy` again. | ||
#### The subscription is already considered destroyed when `onDestroy` is called | ||
This means that you can't call `emit` or `subscribe` in the `onDestroy` callback and that `isDestroyed` will return `true` in the `onDestroy` callback. | ||
## Examples | ||
@@ -240,3 +281,3 @@ | ||
export interface Subscription<T> { | ||
export interface ISubscription<T> { | ||
subscribe: SubscribeMethod<T>; | ||
@@ -248,5 +289,7 @@ unsubscribe: UnsubscribeMethod<T>; | ||
emit: (newValue: T) => void; | ||
destroy: () => void; | ||
isDestroyed: () => boolean; | ||
} | ||
export interface VoidSubscription { | ||
export interface IVoidSubscription { | ||
subscribe: VoidSubscribeMethod; | ||
@@ -258,14 +301,13 @@ unsubscribe: VoidUnsubscribeMethod; | ||
emit: () => void; | ||
destroy: () => void; | ||
isDestroyed: () => boolean; | ||
} | ||
export interface SubscriptionOptions { | ||
export interface ISubscriptionOptions { | ||
onFirstSubscription?: () => void; | ||
onLastUnsubscribe?: () => void; | ||
onDestroy?: () => void; | ||
maxSubscriptionCount?: number; | ||
maxRecursiveCall?: number; | ||
} | ||
export function Subscription<T = void>( | ||
options: SubscriptionOptions = {} | ||
): [T] extends [void] ? VoidSubscription : Subscription<T>; | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25143
421
303