Asynchronous dependency injection using Q promises
MODULE =: cachedatabase: ->: ->
Here we see that constructing a
server object depends on first constructing a
cache object and a
database object. To actually construct a
server object, we need to create an
Injector and call the correspondingly named function on it:
di = require'q-di'injector = MODULEinjectorserver # construct a server (returns a Q promise)
Dependencies can return promises instead of raw values. For example, the following module introduces a 100 ms delay when constructing a
MODULE =: -> Qdelay100then-> 'foo'# Prints "foobar" (after 100 ms):MODULEfoodone consolelogresult
The above example is contrived, but more realistically, some of your components may need to perform IO asynchronously before they are fully initialized. For example, your
server object might depend on a
database object which needs to first establish a connection to the database before it's initialized.
Names of dependency arguments can also be specified explicitly, as shown below. This method for specifying dependencies might be useful if you want to use periods in your dependency names (e.g., to namespace things, such as 'server.backend.cache'):
MODULE =server:args : 'services.cache''services.database': cd# ...
Dependency names containing periods ('.') can be treated hierarchically if the
hierarchical flag is set to
true, as below:
MODULE ='services.component1' : ->'services.component2' : ->'services.component3' :args : 'services.component1''services.component2': c1c2injector = MODULE hierarchical: true# Returns promise for services "container object" containing the 3 components.injectorservices# Returns promise for component 1.injector'services.component1'
This introduces implicit "container object" dependencies into the created injector which will automatically create all dependencies within the container when invoked. Containers can be nested arbitrarily deep:
injector ='foo.bar.baz' : -> ...hierarchical: true# Returns promise for bar container.injectorfoothen -> foobar# Also returns promise for bar container.injector'foo.bar'
This is another feature which can be useful when organizing larger codebases.
All constructed objects are singleton-scoped to the injector. This means that calling a method on an injector multiple times will return the same instance of the object (actually, promise), e.g. the following returns true:
injectorfoo == injectorfoo # always equal
Objects are not shared across injectors, however, e.g. if injector1 and injector2 are separate objects:
injector1foo != injector2foo # never equal
Cyclic dependencies will result in an error being thrown, e.g. the following fails:
: foo # throws error
The Q library is compatible with many other promise implementations (as long as they follow the Promises/A+ specification). This means module dependencies may return non-Q promises. Note, however, that the injector will always coerce such promises into Q promises.