npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

promised-land

v0.2.0

Published

Follow the road to the Promised Land with Bluebird while eating some Bacon on the way.

Downloads

25

Readme

Promised Land

Follow the road to the Promised Land with Bluebird while eating some Bacon on the way.

Build StatusDependencies statusdevDependency Status

NPM

Please note, that this is very basic implementation (but fully functional) of the idea and it definitely needs some polishing and bug fixing. Please check out Trello board * for planned features and next development steps. You can also leave a comment there.*

TL;DR

Stop caring about events being emit too soon. Watch for them with the Promise !

Features

  • Inherited from EventEmitter2.
  • Follow one-time events with Promise.
  • Handle multiple events with Bacon streams.
  • Browser environment compatibility.

Introduction

When writing modular code there is usually need to indirectly communicate with other modules. Having them to know about each other would basically eliminate any attempt to keep them decoupled. You are probably used to apply some kind of pub/sub pattern or event emitters for such situation. That works just fine (when handled properly).

Generally these published events can be divided into two categories:

  • events published only once per application run
  • events repeated multiple times with some data

Do it just once

Category of one-time events is mainly useful in situation when you want to publish information about something being ready to use without really worrying about possible consumers of such information. For example you can have database module taking care of connecting to database server. When the connection is ready, it should publish such information in case something is listening.

The old story

However there is one rather big pitfall. Some of your modules may subscribe to the event too late and miss out that event happening. That means you have to make sure that database module is initialized only after all other dependent modules have subscribed to the event. That can be especially tricky if you are adding some modules to the mix later.

It looks promising

Most viable solution to the issue of being late for the event is called Promise. Basically it means you get container object that gives you the value whenever it's ready. You might ask where to actually obtain such object? Most direct way is to ask the module that is providing it. In that case you are creating some kind of coupling again, although less serious and it can be overcome with good use of design patterns.

Let's roll !

It's time to move this idea little bit further. You want to keep your code modular, but still able to utilize advantages of Promise? It would be great to have some single shared object (similar to emitter) that serves as connection point between modules but doesn't really know about any modules on it's own.And here comes the Promised Land, check this out:

// in your database module nothing new happens...
var Land = require('promised-land');
Land.emit('databaseConnected', db);

// ...however in some consumer module...
var Land = require('promised-land');
Land.promise('databaseConnected').then(function(db) {
	doSomethingWithDatabase();
});

That's right. It's simple as that! You might wonder what is it good for. Well, just emit the event as you are used to and the promised-land will take care of the rest. You can ask for the Promise before event is published or after. That means you don't need to think about any initialization order anymore.

Promise resolution is made when the event is emitted for the first time. Any subsequent emits doesn't change state of the promise nor the value. It comes from the nature of the Promise obviously, but keep this in mind as only one part of your code should emit that particular event.

For the actual Promise implementation I have picked Bluebird library. It's not very well known just yet, but I am actively using it and I love it! Whole library is also made available to you through require('promised-land').Promise so you don't need to actually add dependency to your project. It's up to you.

Repeated events

Now this is much more straightforward and as you may know, promises are not helpful for this at all. Repeated resolution of promise is not simply possible. Promised land is inherited from EventEmitter2. That means you can use any of the methods provided by that library like on or many directly.

Please note, current version of promised-land doesn't support wildcard option of EventEmitter, but it's definitely planned in future versions.

Some Bacon for the breakfast ?

To have a complete package for event handling, I decided to include library BaconJS that is used for FRP (Functional Reactive Programming). I don't have any credits here, I just felt it should be there for the convenience. Just call stream method with event name and you have got yourself full Bacon stream. FRP is just whole new land to explore!

Land.stream('repeatedEvent').onValue(function(val) {
	doSomethingRepeatedly();
});

Bacon library is also made available through require('promised-land).Bacon if you need to create streams on your own.

Usage tips

Learn some other uses of the promised-land.

Protected land

Having the promised-land accessible globally is surely neat, but you may have some privacy concerns here. Anybody can access your land and emit events or steal your promises. But worry not, there is very simple solution!

var Land = require('promised-land');
var myPrivateLand = Land.create();
myPrivateLand.promise('secretEvent');

You can pass myPrivateLand variable around in your code however you like and nobody else can access it. This is basically same approach you might have chosen with your current EventEmitter. You can easily exchange your currently used shared emitter object with private promised-land and everything works like magic!

Reject the promise

In some cases you might want to publish one-time event with some faulty state. Database connection may fail which you might want to handle with application shut down. In that case simply emit event with value being instance of object inherited from the Error.

Land.emit('databaseConnected', new ConnectionError());
// somewhere else...
Land.promise('databaseConnected').then(function(db) {
	workWithDatabase();
}).catch(ConnectionError, function(err) {
	handleError();
});

Multiple promised events

Some of your modules can depend on multiple one-time events being emitted. With promise it's pretty easy, but for the convenience I have included method promiseAll where you can pass any number of event names to obtain one Promise object to watch for all of them.

Land.promiseAll('event1', 'event2', 'event3').then(function(values) {
	doSomethingWhenEverythingIsReady();
});

// or

Land.promiseAll('event1', 'event2', 'event3').spread(function(val1, val2, val3) {
	doSomethingWhenEverythingIsReady();
});

// it is equivalent to...

Promise.all([
	Land.promise('event1'),
	Land.promise('event2'),
	Land.promise('event3')
]).then(function(values) {...})

Transform event from custom emitter

This may be issue for the Bluebird and I am not sure if other libraries have solution, but if you are using Bluebird, you may have encountered situation like this before:

return new Promise(function(resolve) {
	emitter.once('someEvent', resolve);
});

It's so boring to write this all around and increase the unnecessary indentation. You may have some object that emits important event and you want to turn that into Promise. Thanks to the promised-land, you have much more easy way:

return Land.promise('someEvent', emitter);

Just pass in the emitter object in second argument and the promise will be fulfilled from the event within this emitter. This way it skips usual workflow and doesn't care about its internal emitter. Note that the promised-land doesn't record these promises in any way. It's just convenience method to make your code look cleaner and nicer.

Please note: As for now, it looks for once method on passed in emitter object. If there is no once method, exception is thrown. I am planning to make this more transparent in future releases.

Same as for regular workflow, created promise can be also rejected, if event is emitted with Error object.

Land.promise('someEvent', emitter).catch(TypeError, function(err) {
	handleError();
});
emitter.emit 'someEvent', new TypeError('failed event')

Running Tests

  1. Install the development dependencies:

    npm install

  2. Run the tests:

    npm test

Browser support

Download browser bundle from Release tab. These are made using Browserify. It's not optimal for now, but should work correctly. I will add tests for these bundles eventually.