Socket
Socket
Sign inDemoInstall

@nx-js/observer-util

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nx-js/observer-util - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0

2

benchmark/benchmark.js

@@ -304,3 +304,3 @@ 'use strict'

for (let nxSignal of nxSignals) {
nx.unobserve(nxSignal)
nxSignal.unobserve()
}

@@ -307,0 +307,0 @@ })

{
"name": "@nx-js/observer-util",
"version": "1.0.0",
"version": "2.0.0",
"description": "An NX utility, responsible for powerful data observation with ES6 Proxies.",

@@ -5,0 +5,0 @@ "scripts": {

@@ -5,5 +5,5 @@ # The observer utility

The purpose of this library is to allow powerful data observation/binding without any special syntax and with a 100% language observability coverage.
It uses ES6 Proxies internally to create seamless data binding with a minimal interface.
A more detailed blog post about this library can be found
The purpose of this library is to provide transparent reactivity without any special syntax and with a 100% language observability coverage.
It uses ES6 Proxies internally to work seamlessly with a minimal interface.
A blog post about the inner working of this library can be found
[here](https://blog.risingstack.com/writing-a-javascript-framework-data-binding-es6-proxy/) and a comparison with MobX can be found [here](http://www.nx-framework.com/blog/public/mobx-vs-nx/).

@@ -38,3 +38,4 @@

This method creates and returns an observable object. If an object is passed as argument
it wraps the passed object in an observable.
it wraps the passed object in an observable. If an observable object is passed, it simply
returns the passed observable object.

@@ -45,23 +46,19 @@ ```js

### const signal = observer.observe(function, [context], ...[args])
### observer.isObservable(Object)
This method observes a function. An observed function automatically reruns when a property of an
observable, which is used by the function changes (or is deleted). The function doesn't run
immediately on property change, instead it runs after a small delay (when the current stack empties).
Multiple synchronous changes won't cause the function to run multiple times. Changes that result in
no value change (`state.prop = state.prop`) won't cause the function to run either.
The function can observe any synchronous javascript code (nested data, iterations, function calls,
getters/setters, etc.)
Returns true if the passed object is an observable, otherwise returns false.
`observe()` returns a signal, which can later be used to stop the observation. This is similar to
the `const signal = setTimeout()`, `clearTimeout(signal)` pair.
```js
const signal = observer.observe(() => console.log(observable.prop))
const observable = observer.observable()
const isObservable = observer.isObservable(observable)
```
A `this` context and a list of argument can be passed after the observed function as arguments.
In this case the observed function will always be called with the passed `this` context
and arguments.
### const signal = observer.observe(function, [context], ...[args])
This method observes the passed function. An observed function automatically reruns when a property of an observable - which is used by the function - changes (or is deleted). The function doesn't run immediately on property change, instead it runs after a small delay (when the current stack empties).
Multiple synchronous changes won't cause the function to run multiple times. Changes that result in no value change - like `state.prop = state.prop` - won't cause the function to run either.
The function can observe any synchronous javascript code (nested data, iterations, function calls, getters/setters, etc.)
A `this` context and a list of argument can be passed after the observed function as arguments. In this case the observed function will always be called with the passed `this` context and arguments.
```js

@@ -75,38 +72,33 @@ observer.observe(printSum, context, arg1, arg2)

### const signal = observer.queue(function, [context], ...[args])
`observe()` returns a signal object, which can be used to stop or modify the observation.
This method queues a function to be executed together with the currently triggered observed functions. `queue()` returns a signal, which can later be used to remove the function from the queue.
```js
const signal = observer.queue(() => console.log(observable.prop))
const signal = observer.observe(() => console.log(observable.prop))
```
A `this` context and a list of argument can be passed after the queued function as arguments.
In this case the queued function will be called with the passed `this` context and arguments.
### signal.unobserve()
Calling `signal.unobserve()` unobserves the observed function associated with the passed signal. Unobserved functions won't be rerun by observable changes anymore.
```js
observer.queue(printSum, context, arg1, arg2)
function printSum (arg1, arg2) {
console.log(arg1 + arg2)
}
const signal = observer.observe(() => console.log(observable.prop))
signal.unobserve()
```
### observer.unobserve(signal)
### signal.unqueue()
If unobserves the observed or queued function associated with the assed signal.
Unobserved functions won't be rerun by observable changes anymore.
Calling `signal.unqueue()` removes the observed function from the set of triggered and queued observed functions, but it doesn't unobserve it. It can still be triggered and requeued by later observable changes.
```js
const signal = observer.observe(() => console.log(observable.prop))
observer.unobserve(signal)
signal.unqueue()
```
### observer.isObservable(Object)
### signal.exec()
Returns true if the passed object is an observable, otherwise returns false.
Runs the observed function. Never run an observed function directly, use this method instead!
```js
const observable = observer.observable()
const isObservable = observer.isObservable(observable)
const signal = observer.observe(() => console.log(observable.prop))
signal.exec()
```

@@ -123,4 +115,4 @@

// outputs 0 to the console after the stack empties
// the arguments are: observer func, observer func 'this' context, observer func arguments
// outputs 0 to the console
// the passed parameters are: observed func, injected 'this' context, injected arguments
const signal = observer.observe(printSum, undefined, observable1, observable2)

@@ -138,4 +130,4 @@

// finish observing
setTimeout(() => observer.unobserve(signal), 300)
// finishes observing
setTimeout(() => signal.unobserve(), 300)

@@ -167,7 +159,6 @@ // observation is finished, doesn't trigger printSum, outputs nothing to the console

And observer runs after every stack in which the observable properties used by it changes value.
An observer runs maximum once per stack. Multiple synchronous changes of the observable
properties won't trigger it more than once. Setting on observable property without a value change
won't trigger it either.
Every observed function runs once synchronously when it is passed to `observer.observe`.
After that an observed function runs after every stack in which the observable properties used by it changed value. It runs maximum once per stack and multiple synchronous changes of the observable properties won't trigger it more than once. Setting on observable property without a value change won't trigger it either.
```js

@@ -178,3 +169,3 @@ const observer = require('@nx-js/observer-util')

// outputs 'value' to the console after the stack empties
// outputs 'value' to the console synchronously
observer.observe(() => console.log(observable.prop))

@@ -201,3 +192,3 @@

// outputs 'undefined' to the console after the current stack empties
// outputs 'undefined' to the console
observer.observe(() => console.log(observable.expando))

@@ -222,3 +213,3 @@

// outputs 'prop1' to the console after the current stack empties
// outputs 'prop1' to the console
observer.observe(() => console.log(observable.condition ? observable.prop1 : observable.prop2))

@@ -242,3 +233,3 @@

// outputs 'nestedValue' to the console after the current stack empties
// outputs 'nestedValue' to the console
observer.observe(() => console.log(observable.prop.nested))

@@ -260,3 +251,3 @@

// outputs 'Hello World' to the console after the current stack empties
// outputs 'Hello World' to the console
observer.observe(() => console.log(observable.words.join(' ')))

@@ -284,3 +275,3 @@

// outputs 'Hello World' to the console after the current stack empties
// outputs 'Hello World' to the console
observer.observe(() => console.log(observable.greeting + ' ' + observable.subject))

@@ -318,3 +309,3 @@

// outputs 0 to the console after the current stack empties
// outputs 0 to the console
observer.observe(() => console.log(observable.sum))

@@ -370,3 +361,3 @@

// outputs 'name: John, age: 25' to the console after the current stack empties
// outputs 'name: John, age: 25' to the console
observer.observe(() => console.log(`name: ${person.name}, age: ${person.$raw.age}`))

@@ -405,3 +396,3 @@

- 'Function cleanup' tests the cost of disposing observer/listener functions with `disposeFn()` or `nx.unobserve(fn)`.
- 'Function cleanup' tests the cost of disposing observer/listener functions with `disposeFn()` or `signal.unobserve()`.

@@ -408,0 +399,0 @@ Do not worry about the large difference between the vanilla and nx-observe / MobX results.

@@ -17,4 +17,2 @@ 'use strict'

observe,
unobserve,
queue,
observable,

@@ -26,29 +24,26 @@ isObservable

if (typeof fn !== 'function') {
throw new TypeError('first argument must be a function')
throw new TypeError('First argument must be a function')
}
args = args.length ? args : undefined
const observer = {fn, context, args, observedKeys: []}
queueObserver(observer)
const observer = {fn, context, args, observedKeys: [], exec, unobserve, unqueue}
runObserver(observer)
return observer
}
function unobserve (observer) {
if (typeof observer === 'object') {
if (observer.observedKeys) {
observer.observedKeys.forEach(unobserveKey, observer)
}
observer.fn = observer.context = observer.args = observer.observedKeys = undefined
}
function exec () {
runObserver(this)
}
function queue (fn, context, ...args) {
if (typeof fn !== 'function') {
throw new TypeError('first argument must be a function')
function unobserve () {
if (this.fn) {
this.observedKeys.forEach(unobserveKey, this)
this.fn = this.context = this.args = this.observedKeys = undefined
queuedObservers.delete(this)
}
args = args.length ? args : undefined
const observer = {fn, context, args, once: true}
queueObserver(observer)
return observer
}
function unqueue () {
queuedObservers.delete(this)
}
function observable (obj) {

@@ -143,4 +138,6 @@ obj = obj || {}

const observersForKey = observers.get(target).get(key)
if (observersForKey) {
if (observersForKey && observersForKey.constructor === Set) {
observersForKey.forEach(queueObserver)
} else if (observersForKey) {
queueObserver(observersForKey)
}

@@ -164,14 +161,7 @@ }

function runObserver (observer) {
if (observer.fn) {
if (observer.once) {
observer.fn.apply(observer.context, observer.args)
unobserve(observer)
} else {
try {
currentObserver = observer
observer.fn.apply(observer.context, observer.args)
} finally {
currentObserver = undefined
}
}
try {
currentObserver = observer
observer.fn.apply(observer.context, observer.args)
} finally {
currentObserver = undefined
}

@@ -178,0 +168,0 @@ }

@@ -220,3 +220,3 @@ 'use strict'

it('should not run synchronously after registration', () => {
it('should run synchronously after registration', () => {
let dummy

@@ -231,11 +231,7 @@ const observable = observer.observable({prop: 'prop'})

expect(numOfRuns).to.equal(0)
expect(dummy).to.equal(undefined)
expect(numOfRuns).to.equal(1)
expect(dummy).to.equal('prop')
return Promise.resolve()
.then(() => {
expect(numOfRuns).to.equal(1)
expect(dummy).to.equal('prop')
})
.then(() => {
observable.prop = 'new prop'

@@ -565,3 +561,3 @@ })

describe('execution order', () => {
it('should run in registration order the first time', () => {
it('should be first-tigger order', () => {
let dummy = ''

@@ -571,19 +567,2 @@ const observable = observer.observable({prop1: 'prop1', prop2: 'prop2', prop3: 'prop3'})

observer.observe(() => dummy += observable.prop1)
observer.queue(() => dummy += observable.prop2)
observer.observe(() => dummy += observable.prop3)
observable.prop2 = 'p'
observable.prop1 = 'p1'
observable.prop3 = 'p3'
observable.prop2 = 'p2'
return Promise.resolve()
.then(() => expect(dummy).to.equal('p1p2p3'))
})
it('should run in first-tigger order after the first time', () => {
let dummy = ''
const observable = observer.observable({prop1: 'prop1', prop2: 'prop2', prop3: 'prop3'})
observer.observe(() => dummy += observable.prop1)
observer.observe(() => dummy += observable.prop2)

@@ -606,109 +585,98 @@ observer.observe(() => dummy += observable.prop3)

describe('queue', () => {
it('should unobserve the observed function', () => {
let dummy
const observable = observer.observable({prop: 0})
describe('observer methods', () => {
describe('exec', () => {
it('should track the newly discovered function parts', () => {
let condition = false
let counter
let numOfRuns = 0
function test() {
dummy = observable.prop
numOfRuns++
}
const signal = observer.observe(test)
const observable = observer.observable({condition: false, counter: 0})
const signal = observer.observe(conditionalIncrement)
return Promise.resolve()
.then(() => observable.prop = 'Hello')
.then(() => observer.unobserve(signal))
.then(() => observable.prop = 'World')
.then(() => observable.prop = '!')
.then(() => expect(numOfRuns).to.equal(2))
function conditionalIncrement () {
if (condition) {
counter = observable.counter
}
}
return Promise.resolve()
.then(() => expect(counter).to.be.undefined)
.then(() => observable.counter++)
.then(() => expect(counter).to.be.undefined)
.then(() => {
condition = true
signal.exec()
})
.then(() => expect(counter).to.equal(1))
.then(() => observable.counter++)
.then(() => expect(counter).to.equal(2))
})
})
it('should unobserve even if the function is registered for the stack', () => {
let dummy
const observable = observer.observable({prop: 0})
describe('unqueue', () => {
it('should remove the observed function from the queue', () => {
let dummy
const observable = observer.observable({prop: 0})
let numOfRuns = 0
function test() {
dummy = observable.prop
numOfRuns++
}
const signal = observer.observe(test)
let numOfRuns = 0
function test() {
dummy = observable.prop
numOfRuns++
}
const signal = observer.observe(test)
return Promise.resolve()
.then(() => {
observable.prop = 2
observer.unobserve(signal)
})
.then(() => expect(numOfRuns).to.equal(1))
return Promise.resolve()
.then(() => {
observable.prop = 2
signal.unqueue()
})
.then(() => expect(numOfRuns).to.equal(1))
})
})
})
describe('unobserve', () => {
it('should unobserve the observed function', () => {
let dummy
const observable = observer.observable({prop: 0})
describe('unobserve', () => {
it('should unobserve the observed function', () => {
let dummy
const observable = observer.observable({prop: 0})
let numOfRuns = 0
function test() {
dummy = observable.prop
numOfRuns++
}
const signal = observer.observe(test)
let numOfRuns = 0
function test() {
dummy = observable.prop
numOfRuns++
}
const signal = observer.observe(test)
return Promise.resolve()
.then(() => observable.prop = 'Hello')
.then(() => observer.unobserve(signal))
.then(() => {
expect(signal.fn).to.be.undefined
expect(signal.context).to.be.undefined
expect(signal.args).to.be.undefined
expect(signal.observedKeys).to.be.undefined
})
.then(() => observable.prop = 'World')
.then(() => observable.prop = '!')
.then(() => expect(numOfRuns).to.equal(2))
})
return Promise.resolve()
.then(() => observable.prop = 'Hello')
.then(() => signal.unobserve())
.then(() => {
expect(signal.fn).to.be.undefined
expect(signal.context).to.be.undefined
expect(signal.args).to.be.undefined
expect(signal.observedKeys).to.be.undefined
})
.then(() => observable.prop = 'World')
.then(() => observable.prop = '!')
.then(() => expect(numOfRuns).to.equal(2))
})
it('should unobserve even if the function is registered for the stack', () => {
let dummy
const observable = observer.observable({prop: 0})
it('should unobserve even if the function is registered for the stack', () => {
let dummy
const observable = observer.observable({prop: 0})
let numOfRuns = 0
function test() {
dummy = observable.prop
numOfRuns++
}
const signal = observer.observe(test)
let numOfRuns = 0
function test() {
dummy = observable.prop
numOfRuns++
}
const signal = observer.observe(test)
return Promise.resolve()
.then(() => {
observable.prop = 2
observer.unobserve(signal)
})
.then(() => expect(numOfRuns).to.equal(1))
})
it('should remove queued function', () => {
let dummy
const observable = observer.observable({prop: 0})
let numOfRuns = 0
const signal = observer.queue(() => {
dummy = observable.prop
numOfRuns++
return Promise.resolve()
.then(() => {
observable.prop = 2
signal.unobserve()
})
.then(() => expect(numOfRuns).to.equal(1))
})
observer.unobserve(signal)
return Promise.resolve()
.then(() => expect(numOfRuns).to.equal(0))
.then(() => {
expect(signal.fn).to.be.undefined
expect(signal.context).to.be.undefined
expect(signal.args).to.be.undefined
expect(signal.observedKeys).to.be.undefined
})
})
})
})

Sorry, the diff of this file is not supported yet

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