Git Product home page Git Product logo

js-signals's Introduction

JS-Signals

Build Status

Custom event/messaging system for JavaScript inspired by AS3-Signals.

For a more in-depth introduction read the JS-Signals Project Page and visit the links below.

Links

License

Distribution Files

You can use the same distribution file for all the evironments, browser script tag, AMD, CommonJS (since v0.7.0).

Files inside dist folder:

  • docs/index.html : Documentation.
  • signals.js : Uncompressed source code with comments.
  • signals.min.js : Compressed code.

You can install JS-Signals on Node.js using NPM

npm install signals

CompoundSignal

Note that there is an advanced Signal type called CompoundSignal that is compatible with js-signals v0.7.0+. It's useful for cases where you may need to execute an action after multiple Signals are dispatched. It was split into its' own repository since this feature isn't always needed and that way it can be easily distributed trough npm.

CompoundSignal repository

Repository Structure

Folder Structure

|-build       ->  files used on the build process
|-src         ->  source files
|-tests       ->  unit tests
`-dist        ->  distribution files
  `-docs        ->  documentation

Branches

master      ->  always contain code from the latest stable version
release-**  ->  code canditate for the next stable version (alpha/beta)
develop     ->  main development branch (nightly)
**other**   ->  features/hotfixes/experimental, probably non-stable code

Building your own

This project uses Apache Ant for the build process. If for some reason you need to build a custom version of JS-Signals install Ant and run:

ant build

This will delete all JS files inside the dist folder, merge/update/compress source files, validate generated code using JSLint and copy the output to the dist folder.

There is also another ant task that runs the build task and generate documentation (used before each deploy):

ant deploy

IMPORTANT: dist folder always contain the latest version, regular users should not need to run build task.

Running Tests

The specs work on the browser and on node.js, during development you can use the spec/runner_dev.html file to avoid doing a build every time you make changes to the source files. On node.js you need to run ant compile after each source file change otherwise npm test will execute the files from last build - not adding it as a pretest script since the build adds information about the build date and build number and that would pollute the commit history.

js-signals's People

Contributors

conradz avatar millermedeiros avatar paullewis avatar tomyan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

js-signals's Issues

option to "curry" a listener?

in some cases I fell that the add() and addOnce() methods are missing an option to pass default parameters to the listener function..

e.g:

myObject.disposed.add(otherScope.doSomething, otherScope, 0, 'foo', false, 123);
myObject.disposed.dispatch('bar', 456);

would call otherScope.doSomething() passing 'foo', false, 123, 'bar', 456 as params.

kinda similar to the way that setTimeout works on all browsers but IE...

the problem of doing something like this is that we couldn't add new params to those methods afterwards without some awkward overload.

add support to dispatch history / "message bus"

One nice feature of deferred/promises is that you can add listeners after a promise was already resolved and it will still pass arguments supplied on the resolve call...

Postman have a feature called "message bus" which also keeps record of last dispatch and gives the option to listen to a message previously dispatched.

It may be a nice addition to signals as well, but I think the default behavior should remain the same, getting value from a previously dispatched signal should be an opt-in feature.

use a public property to enable/disable signal/binding instead of methods

at the beginning I wasn't sure how the code would evolve and decided to wrap access to the _isEnabled property (to increase flexibility) but I don't see any reason for keeping it since they are just setting/getting boolean values and could be simplified to a single boolean property.

until now best name seems to be active since it represents estate, don't confuse with an action call or a signal name.

release v1.0.0

it's been almost 2yrs since v0.1.0 was released. All API changes since v0.6 (2011/04/09) was backwards compatible, which for me is enough to say that API shouldn't change drastically.

Let's plan v1.0.0 release to 2012/11/26 so we complete 2 full years and have 1mo to review v0.9.0 (we will probably just bump the version number).

convert signal instance into a function

functions are first class objects in JavaScript, I find it unnecessary that we need to call myObj.mySignal.add(handler) when we could simply do it like myObj.mySignal(handler).

mySignal() would be just a shortcut for mySignal.add() (we wouldn't remove mySignal.add).

PS: this is just an idea that I had based on the amount of mistakes I commit while trying to add a listener, specially when the signal have a name like clicked or started.

I will wait for feedback.

calling Signal methods after `Signal.dispose()` throws errors

after dispose _bindings array is deleted so trying to add/remove/dispatch throws errors. is this the proper behavior?

So far I think it is the proper behavior since dispose is supposed to destroy the object and it is documented. Adding new listeners and dispatching should definitely throw errors, not sure about other methods..

maybe improve error message, something like: "Signal was previously disposed. You can't add new listeners or dispatch it.".

issue #25 is also related.

use a linked list to store bindings and check perf

That is something I've been considering ever since v0.1, LinkedLists are usually faster in some types of operations because of the way they are constructed (no need to move all the items of the list to insert one in the middle). I didn't implemented it yet because it only makes sense if the amount of listeners to a single event is HUGE and that usually isn't the case (specially since each Signal is an individual event type), on all the times I used Signals on the past 1yr 1/2 (n) was always small (under 100).

My current feeling is that the amount of code wouldn't pay-off the perf improvement on the edge case of having thousands of listeners to the same Signal, I'm also not sure how it would affect perf in a small (n) (regular scenario).

Maybe keep it as a branch so user can choose which version to use in his case.

remove named functions

never had any issue with it (specially since I'm using same names and inside a closure) but don't think worth using it, because all the known IE issues ( http://kangax.github.com/nfe/ ), it sucks to type everything twice and I don't think the stack trace worth the trouble in such simple system.

maybe remove JSDoc and create only external documentation

JSDoc add too much noise to the source code and don't add that much benefits.. variable and methods names are pretty straightforward so most of the comments are redundant... maybe refactor code on a way that comments are almost unnecessary.

advantages of jsdocs :

  • reduce the chance of the documentation getting outdated (since you remember to update it while you edit the code)..
  • having typed annotations helps closure compiler to find errors.. (not that unit tests together with JSLint wouldn't catch it)

disadvantage:

  • too much "noise", code gets harder to read.
  • most IDEs/editors don't use JSDocs for code completion and/or error checking, making it almost useless during development.
  • external documentation is way more flexible, can have more detailed information and better for translation purposes.

unsure about `SignalBinding.listenerScope` naming

other options (for now):

  • scope
  • context
  • handlerContext / listenerContext
  • executionContext

for now context seems to be the best name, since it actually isn't the "function scope" but its execution context (this object), not sure if it will collide with other features in the future or become confusing.

validate code using JSLint

I don't agree with all the rules from JSLint but it seems that a bunch of people take it very serious..

rules I won't follow:

  • strict white space
  • Disallow dangling _ in identifiers
  • Disallow ++ and --
  • Require "use strict";

but I should make it validate through the other rules, even knowing that right now the validation is not catching any real problem, just code-style preferences..

NPM?

Have you tried this out with nodejs before? You should get a version into the nodejs package management tool http://npmjs.org/

example w/ node

Is there an example of using signals in node server side?

signals.CompoundSignal / signals.when() / signals.join()

sometimes it can be useful to have a feature similar to jQuery.when and Q.join - execute a callback after all the promises are resolved - proposed API:

//alias for brevity
var CompoundSignal = signals.CompoundSignal;

// CompoundSignal groups multiple signals into a single instance
var completedManyThings = new CompoundSignal( myObj.endedAnimation, otherObj.completedSomething );

// CompoundSignal is just a regular Signal that gets dispatched after the other
// signals are dispatched (and have a similar API)
completedManyThings.addOnce( doSomethingOnCompletedManyThings );

function doSomethingOnCompletedManyThings(paramsArr1, paramsArr2){
  //handler would receive parameters of each signal as arrays since 
  //they can dispatch an arbitrary number of parameters
  console.log( paramsArr1, paramsArr2 );
}

maybe also create an alias:

//`when` returns a `CompoundSignal` (that's why we can chain addOnce)
signals
    .when( myObj.endedAnimation, otherObj.completedSomething )
    .addOnce( doSomethingOnCompletedManyThings );

rename `SignalBinding.params` to `SignalBinding.args`?

the main reason why I didn't used it in the first place is because I didn't want to create confusion with the arguments object, but every time I use this property I feel the name is wrong since you are setting which arguments are going to passed as the first ones to the handler...

getters and setters for memorize/active/params? Object.freeze by default?

I kinda regret of using normal properties instead of getter/setters because JS doesn't complain when we try to set a property that doesn't exist - signal.memoize = true; will be extremely hard to spot (missing r).

It could be fixed by "use strict" + Object.freeze() on ES5:

"use strict";

var changed = new Signal();
Object.freeze(changed);

changed.memoize = true; // throw error since `memoize` doesn't exist

I heard that Object.freeze have some perf impact on V8 but that should be fixed soon.

One option would be to freeze the Signal and SignalBinding automatically during the instantiation but that might prevent users from adding custom behavior to a Signal instance (will reduce the flexibility).

My current opinion is that we should leave it as is to keep backwards compatibility and maybe do getters/setters for a v2.0 release.

any method that access internal properties from the SignalBinding throws errors after calling `SingalBinding.detach()`

as of v0.6.2 the second time you call detach on a binding it throws an error since the binding doesn't have the _signal and _listener properties anymore. I think the proper behavior would be to simply return null instead of throwing an error.. (detach() usually returns the listener)

I think it doesn't make any sense for a binding exist without a listener/signal but throwing errors like this ain't fun or necessary.. (specially if calling detach multiple times..)

maybe add a new method like isBound() that checks if Binding still references a Signal..

remove `dispose` method from `SignalBinding` since `detach` does the same thing

the SignalBinding already have the detach method which does exactly the same thing - code is even duplicated ( _destroy is already being called by Signal during removal)

maybe keep it just for consistency - since Signal also have a dispose method - but make it be just an alias to the detach method and document that it is just an alias.

so far I think it is better to just kill it completely, detach is a better name anyway and I doubt that a lot of people is using it.

make `isDef()` private

no point on making it public.. users shouldn't count on it. this feature is completely unrelated to the library purpose.

overload `add()` and `addOnce()` methods w/ config object

some of the new proposed features (issues #28 and #29) would need some sort of method overloading or add() and addOnce() will start having too many parameters (which is bad).. maybe we should just add an overload to those methods which accepts a single Object parameter with all the settings/properties that should be set to the binding.

e.g.

myObj.clicked.add({
  listener : doSomething,
  context : otherScope,
  priority : 5,
  active : false,
  ...
});

didn't added this option since the beginning because I thought it would be an unnecessary overhead, but as js-signals get more features it seems that it would be a flexible and concise way of setting multiple properties.

Do away with 'signals' namespace?

At present JS-Signals use the signals namespace to avoid global pollution. However, as with v0.7.4 of the library, the only property exported to this namespace is the Signal constructor function for creating a new Signal object. Is there a strong argument against dropping the signals namespace and just exporting the Signal constructor instead?

This would make importing Signals with RequireJS tidier, ie:

var Signal = require('vendor/signals');
var mySignal = new Signal();

SignalBinding.listener should be private

if the user switch the SignalBinding.listener at runtime to a handler already bound to a Signal it will be dispatched twice and will need to call remove twice. Probably create a getter and setter function that checks if handler already exists.

PS: I've never tested it.

break CompoundSignal into it's own file?

I don't think that CompoundSignal will be that useful in most cases.. considering to split it into it's own file, maybe even into it's own project (so it can be published to npm as a different package)..

I'm really divided about it since file size isn't a big deal after minification + gzip..

with: 4.24KB (1.53KB gzipped)
without: 2.99KB (1.2KB gzipped)
diff: 1.24KB (0.33KB gzipped)

PS: an additional file request is way worse than 1.2 KB but I also find it strange to force everyone to load something that just a few may use..

see issue #34

add some easy way to bind Signal.dispatch()

one thing that I always have to do is to make sure Signal.dispatch() is called on the right context, specially when it needs to be called on a callback, eg:

$('#foo').fadeOut(500, ended.dispatch); // won't work!

The case above doesn't work because this inside dispose() points to the wrong object (jQuery calls the callback passing the element as this). A solution is to create another function that is bound to the proper context:

// works
$('#bar').fadeOut(500, dispatchEnded);

function dispatchEnded(){
  ended.dispatch();
}

The main reason why I didn't auto bind the dispatch() context is because I was unsure if it would make it harder to subclass another signal, but we can simply reuse the method of the prototype like this:

function Signal(){
  var self = this;
  this.dispatch = function(){
    Signal.prototype.dispatch.apply(self, arguments);
  };
  // ...
}

If the user can instantiate a Signal he can also access the Signal.prototype which is enough to grab the unbound method and reuse it (like CompoundSignal does).

Overall I think it will avoid more problems than it will cause, specially since most people don't understand how scope works in JS and the advanced users will know a way around it (call the prototype method directly).

better name for Signal.memorize

memorize sounds like an action so user may think it is a method instead of a boolean.. couldn't find a better name that was a single word... opinions/proposals are welcome!

reset(), restart(), memorize and CompoundSignal

just realized that reset() isn't a good name for clearing the memorized arguments.. CompoundSignal also needs a way to reset the stored arguments and mark it as unresolved..

so far I have reset() for clearing the memorized arguments and restart() to restoring the CompoundSignal to it's original state.. I think the names are too similar and it will be confusing.

See: #34 and #29

rename dist files to signals.js

since NPM package is called signals I think all files should just be called "signals" instead of "js-signals". "js-signals.js" is redundant.

add method .has(listener):Boolean

right now we have an internal method _indexOfListener(listener) so implementing a has(listener) is pretty trivial... it can be useful in some cases...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.