MangoJuice Core
Reference
Table of Contents
- Basic
- Extras
Basic
The most common APIs that you will work with most of the time
Block
Block is a top level component in MangoJuice which consists of three parts:
- Model factory function
createModel
which should return a plain object. The main requirement for this function is that it should be able to return some model with zero arguments. But it can also accept some arguments as well. - A logic class which implements LogicBase interface
- A View function which is aimed to render the model created by
createModel
The View part is optional and it could have different type depending on selected View library to render a model and particular binding library.
Properties
Logic
LogicBase A logic class that should be attached to the model.createModel
Function A function that shuld return a plain object which will be an init model of the block.View
any? Something that some particular view library can render
Examples
// You can define block in short way using an objectconst MyBlock = field: 1 Logic: \@cmd { return field: thismodelfield + 1 ; } <div>modelfield</div> ; ;
// Or you can split it with modules (recommended)// ./MyBlock/index.js;;; View Logic createModel ;;; // ./MyBlock/Logic.js;; <Model> \@cmd { return field: thismodelfield + 1 ; } // ./MyBlock/Model.js;const createModel = field: 1; // ./MyBlock/View.jsconst MyBlockView = <div>modelfield</div>;; // ./index.js;; ;
LogicBase
Basic class for every logic. It is not necessaty to inherit an actual logic class from it, but for better type-checking it could be really usefull.
Logic class defines:
- A set of commands that could change an associated model – methods decorated with cmd
- What fields of the model should be associated with which logic class (children blocks) – LogicBase#children method
- A set of computed model fields – LogicBase#computed method
- A set of rules to handle commands from children logic – LogicBase#hubAfter and LogicBase#hubBefore methods
- A logic to handle external events – LogicBase#port method.
The logic class defines all the business-logic of some part of your application. The main goal of the logic – change associated model. Check cmd and Process#exec to have a better understanding how command can be defined and how it is executed.
Properties
model
Object A model object which is associated with the logic class in parent logic.meta
Object An object defined in LogicBase#config method for storing some internal logic state.shared
Object An object with some shared state, defined while run process of the app.
Examples
// ./blocks/MyBlock/Logic.js// @flow;;; <Model> { return child: ChildBlockLogic ; } { if cmd return this; } \@cmd { return field: arg + thismodelfield ; }
config
A function that defines init commands and meta object of the
logic. Can accept any type and number of arguments. Config
will take arguments passed to child function (for example
child(MyLogic, 1, 2, 3)
defines that the config
method of
MyLogic
will be invoked like config(1, 2, 3)
)
Could return an object with the fields:
initCommands
which could be an array or a single Command, that will be executed every time when the logic instance created and associated with some model (when Process#run invoked)meta
which could be an object with some internal state of the logic
Examples
{ return initCommands: thisStartThis this meta: something: propsamount ;}
children
This function defines what logic class should be associated with which model field. Should return an object where a key is a model field name, and a value is a logic class or an object that defines logic with arguments (return from child function)
Examples
{ return searchForm: searchResults: SearchResultsLogic ;}
Returns Object
computed
Should return an object which defines computed fields of the model. A key of the object is a model field, and value is a function or computed function definition object (return from depends function).
Computed field is lazy, which means that it is computed on the first use of the field and the computation result cached until the next update of the model.
If you want to define a computed field which ueses in computation not only fields from the own model, but maybe some values from child model, or from shared model, then you will need to use depends function to define what models used while computation to observe changes of these models to trigger an update event of the own model (to update views).
Examples
{ return thismodela + thismodelb withDeps: ;}
Returns Object
port
A function to handle global event, such as browser-level events,
websocket, intervals. The main porpouse of the port
is to subscribe
to some global events, execute appropriate commands from the logic
on the events and remove the handlers when the logic was destroyed.
Parameters
exec
Function It is a function that execute a passed commanddestroyed
Promise A promise that will be resolved when the logic destroyed
Examples
{ const timer = ; destroyed;}
hubAfter
Hub for all commands executed in the children blocks. The only
arguments is a command object that was executed.
Could return a Command or an array of Commands that should
be executed next.
Most of the time this function will look like a switch
,
but maybe with some additional rules for the cases. Take a look
to Command#is function to have a better understanding how you
can determine that the command is exactly what you are waiting for.
Parameters
cmd
Command Command that was executed in some child logic or in any child of the child and so on down to the leaf of the tree.
Examples
{ if cmd return this; if cmd return this thisAnotherHandler ; }
Returns (Command? | Array<Command>?)
hubBefore
Same as LogicBase#hubAfter, but catch commands before it will be actually executed, so you can compare prev and next model state for example.
Parameters
cmd
Command
Returns (Command? | Array<Command>?)
cmd
Decorator that converts a logic method to a command factory. The result function (command factory function) is a function that return a Command instnace with the decorated function and arguments that you will pass to a command factory function.
You can use this decorator without arguments or with an options argument (object)
Check Process#exec to see what the origin function could return to do something. If in short, it can return: nothing, command (or factory or array of commands), task, model update object.
Parameters
obj
Object If only this argument passed then it will be considered as options object which can customize command behaviour.obj.debounce
number If greeter than zero then an exeuction of the command will be debounced by given amount of millisecondsobj.throttle
number If greeter than zero then an exeuction of the command will be throttled by given amount of millisecondsobj.noInitCall
bool If true then debounced/throttled execution won't start with init command call. By default debounced/throttled command will be instantly exected and only then wait for a chance to execute again. WithnoInitCall=true
there is no instant init command call.obj.internal
bool Make the command invisible for parent LogicBase#hubBefore and LogicBase#hubAfter. Use it for commands that shouldn't be handled by hubs to increse performance.obj.name
string By default name of the command taken from origin function. With this option you can override the name with any other value.
methodName
descr
Examples
\@cmd { // do something }
\@ { // if you will call this command 3 times every // 100ms then it will be executed 2 times, // first right when you call it for a first time, // and second in 600ms. }
\@ { // if you will call this command 3 times every // 100ms then it will be executed 2 times, // first right when you call it for a first time, // and second in 300ms. }
\@ { // if you will call this command 3 times every // 100ms then it will be executed 1 time in 300ms. }
\@ { // this command can't be caught in `hubBefore` or `hubAfter` // in parent logics. Use it carefully. }
Returns (Function | object) Command factory function (if all three arguemnts passed) or a decorator function with binded options object (that you pass as a first argument)
run
Run given block. It is a short-hand function for running bind
and then running a Process by Process#run.
Also it returns an additional finished
Promise which is resolved
when all async tasks finished and all handler commands executed.
Parameters
Returns {proc: Process, model: Object, block: Block, finished: Promise} An object
which is almost the same as returned from bind, but with
additional finished
field, which is a Promise that will be resolved
when all blocks will execute all commands and all async tasks will be
finished.
mount
Mount running block. As third and next arguments you can pass other running blocks which will be stopped with the main running block (useful for HMR).
Parameters
mounter
Mounter An implementation of Mounter interfacerunRes
{proc: Process, block: Block, model: Object} An object that returned from run function.otherBlocks
...any
Examples
const MyBlock = field: 1 Logic: \@cmd { return field: thismodelfield + 1 ; } <div>modelfield</div> ; ;
Returns {view: any, stop: Function} An object with the result form Mounter#mount
function (in view
field) and stop function which destroy the process and
unmount a view using Mounter#unmount
task
Creates a TaskMeta object that could be returned from async task command. It describes the task that should be executed.
For more information what the Task is and how it should be implemented take a look to TaskMeta.
Parameters
taskFn
Function A function that could return a Promise
Properties
CANCEL
string A symbol that you can use to make a promise "cancellable"
Returns Object
cancel
This function should be used to cancel async task execution. Also it can cancel the debounced/throttled command.
- In case with task, if task was executing while you call a cancel command, the task will be cancelled (execution stopped). Otherwise nothing will happen.
- In case with debounced/throttled command, if the command scheduled to be executed then the schedule timer will be cleared and the command won't be executed. If the command is not scheduled then nothing happens.
To use it just pass a command or command factory as a first argument and return it from some other command.
Parameters
cmd
(Command | function) Command or command factory that you wan to cancel. Could be a command that returns task or debounced/throttled command
Examples
\@cmd { return ; } \@cmd { return ; } \@ { // do something }
Returns Command Returns a new command that will cancel the execution of the command passed as an argument.
child
Creates an object which describes child logic which will be attached to some model field. Should be used in LogicBase#children to define a logic that should be instantiated with some arguments passed to LogicBase#config.
Parameters
Examples
{ return meta: amount ; } { return modelField: // `config` of `ChildLogic` will be invoked with // `10` as a first argument }
Returns Object Object that contains a logic class and an arguments array that
should be used to invoke config
method of provided logic class.
depends
Function that helps to describe a computed field with external model dependncies. Should be used in LogicBase#computed. It creates an instance of ComputedField with dependencies passed as arguments.
A compute field with external dependencies is a field that should be updated (re-computed) not only when the own model udpated, but also when depdendency models updated.
Parameters
deps
...deps A list of models with attached logic
Examples
// some logic { return childModel: ChildLogic ; } { return // depends on `childModel` computedField: // depends on `shared` model computeWithShared: }
Returns ComputedField
Extras
Other pieces of MangoJuice
Process
The main class of MangoJuice that ties model, logic and view together.
It works in the following way:
- You create a model for your app (object) using
createModel
of the root block. The result is a plain object. - Then you need to create an instance of Process and pass logic class in the options object.
- Then you
bind
the Process instance to the model object you created on step one. "To bind Process to a model" means that in the model will be created a hidden field__proc
with a reference to the Process instance. - After that you can
run
the Process, which will execute init commands, port.
bind
and run
also look at the object returned from LogicBase#children
and create/run Process instances for children models.
Most of the time you do not need to care about all of these and just use run and mount. These small functions do everything described above for you.
Examples
; const ExampleBlock = { return a: 10 ; } Logic: \@cmd { return a: thismodela + amount ; } ; const model = ExampleBlock;const proc = logic: ExampleBlockLogic ;proc;proc;proc;
bind
Bind the process instance to a given model, which means that hidden
__proc
field will be created in the model with a reference to the
Process instance.
Also bind all children models – go through children definition returned by LogicBase#children, create Process for each child and bind it to an appropreat child model object.
Parameters
model
Object A model object that you want to bind to the Process.
run
Run the process – run children processes, then run port and init commands defined in config. Also run all model observers created by observe
destroy
Destroy the process with unbinding from the model and cleaning
up all the parts of the process (stop ports, computed fields).
__proc
field will be removed from the model object and all
children objects.
Parameters
deep
Boolean If true then all children blocks will be destroyed. By default, if not provided then considered as true.
exec
Exec a command in scope of the Process instance. It will use binded model and logic object to run the command. The command could be function (command factory) or object (Command instance).
A command origin function could return three types of things:
- Another Command/command factory or an array of commands (an element of the array also could be another array with commands or null/undefined/false). All commands will be execute in provided order.
- An instance of TaskMeta (which is usually created by task helper function). The returned task will be started and do not block execution of next commands in the stack.
- A plain object which is a model update object. The object will be merged with current model.
If command is undefined or Process
instance not binded
to any model it will do nothing.
Parameters
Examples
\@cmd { return 1 > 2 && thisSomeCoomand this 2 > 1 && thisAndSomeOtherCommand this ; } \@cmd { return } \@cmd { if Math > 06 return field: Date ; }
finished
Returns a promise which will be resolved when all async tasks and related handlers in the process and all children processes will be finished.
Returns Promise
Command
Class which declares a command that will be executed in the future by some Process#exec. It contains a function that will be executed, arguments that should be passed to the function and command name.
The command also keep a context that should be used to execute a function. Usually this context is some logic instance. You can bind a context to the command using Command#bind function.
The command instance is immutable, so any function that makes some change in the command will produce a new command instead of changing the original one.
Parameters
Properties
func
Function An origin function that should be executedargs
Array<any> A set of arguments that should be used to execute the fucntion.
exec
Execute a function stored in the command instance. It passes stored arguments to the function and call it in stored context. Returns the value that was returned form the function.
Returns any
clone
Clone instance of the command
Returns Command
is
Check is the command equal to some other command or have a reference to the same origin function. Optionally you can also check that the command binded to some concrete model (using the second argument).
You can use many different formats to define the command to check:
- Just a string, which should be constructed like
Logic Class Name + Function Name
- Command object or command factory that you can get from prorotype of some logic class.
- Binded command object or command factory that you can get from concrete model using logicOf function
Parameters
cmd
(Command | string) Command object or command factory or command namechildModel
Object? Model object that should be binded to the command (optional)
Examples
\@cmd {} { if cmd // by command name if cmd // by command factory if cmd // by binded to a concrete model command factory (commands // binded for other models will be ignored) if cmd // by command factory and concrete child model (commands // binded for other models will be ignored) }
Returns Boolean Returns true
if the command have same name, or same origin function
and binded to same model (if second argument provided)
appendArgs
Creates a new Command instance (clone the command) and append given list of arguments to the current list of arguments. Returns a new Command with new list of arguments.
Parameters
args
Array<any> List of arguments that will be appened to tne new Command
Returns Command New command with appended arguments
bind
Creates a new Command instance and set given logic instance in it. Also update command name by name of the origin function and name of the logic class.
Parameters
logic
LogicBase A logic instance that should be binded to a command
Returns Command New command binded to given logic instance
TaskMeta
Class for declaring an async task. An instance of this object returned from taks function.
A task function is a function that could return a Promise. The resolved value will be passed to a success command handler, the rejected command will be passed to a fail command handler.
If task function do not return a Promise, then the returned value passed to a success command, and if the function throw an error then the error passed to a fail command.
The task function receive at least one argument – an object with model
, shared
and meta
. All the next arguments will be given from a command that returned a task,
if it is not overrided by TaskMeta#args
By default a task is single-threaded. It means that every call to a task will cancel previously running task if it is running. You can make the task to be multi-threaded by TaskMeta#multithread, so every call to a task won't cancel the running one.
In a context of the task function defined special call
function. This function should
be used to invoke sub-tasks. It is important to use call
to run sub-tasks because
call
create a cancellation point of the task – if the task cancelled, then nothing
will be executed after currently running call
sub-task.
call
returns an object with result
and error
fields. If error
field is not
empty, then something weird happened in the sub-task. call
internally have a try/catch,
so it is not necessary to wrap it with try/catch, just check the error
field in the
returned object and if it is not empty – handle it somehow (throw it, or do any custom
error handling)
Also in a context of the task defined a notify
function. It is a function that execute
notify command if it is defined. The command executed with the same arguments as passed
to notify
function. You can use it to incrementally update a model while the long
task is executing, to show some progress for the user.
Parameters
Examples
{ this; await this; return a / b * c;} { const result error = await this; if error throw 'Something weird happened in subtask' return result + 10;}
\@cmd { return }
notify
Set a notify handler command. This command executed by call of this.notify
inside a task with the same arguments as passed to this.notify
Parameters
cmd
Command
Returns TaskMeta
success
Set a success handler command. Will be executed with a value returned from the task, or if the task returned a Promise – with resovled value.
Parameters
cmd
Command
Returns TaskMeta
fail
Set a fail handler command. Will be executed with error throwed in the task, or if the task returned a Promise – with rejected value.
Parameters
cmd
Command
Returns TaskMeta
multithread
Define the task to be "multi-thread", so every call will run in parallel with other calls of the same task in scope of one process (model).
Parameters
val
boolean
Returns TaskMeta
engine
Set task executor function. The executor function should return an object
that should have at least two fields: exec
and cancel
functions.
exec
should return a promise which should be resolved or rejected
with an object { result, error }
. A cancel
function should stop
the task execution.
Parameters
engine
Function A function that returns{ exec: Function, cancel: Function }
object
Returns TaskMeta
args
Override arguments which will be passed to the task starting from second argument. By default a task will receive the same set of arguments as a task command. If this function invoked, then the task will receive given arguments instead of command arguments.
Parameters
args
...any
Returns TaskMeta
DefaultLogger
A class which defins a logger for tracking command execution process. The default implmenetation just defines all possible logger methods (log points) and print errors to the console. Extend this class to define your own logging functionality (like logging errors with Setnry or Loggly)
Examples
// ./index.js;; // Implement custom logger { Raven; } // Pass instance of the Logger to `run` options object.;
onStartExec
This method invoked right before anything else - as the first thing in Process#exec. Even before any LogicBase#hubBefore.
Parameters
cmd
Command Command that just started execution process
onStartHandling
Invoked right before command will go through all LogicBase#hubBefore or LogicBase#hubAfter up in the blocks tree. The second argument indicates is it called for LogicBase#hubBefore or for LogicBase#hubAfter.
Parameters
cmd
Command Command that is going to go through hubs in parent blocksisAfter
Boolean If true then the command is going through "after" hubs, "before" otherwise
onEndHandling
Invoked when the command went through all hubs and when all sync commands returned from hubs was executed.
Parameters
cmd
Command Command that went through all hubsisAfter
Boolean If true then the command going through "after" hubs, "before" otherwise
onExecuted
Invoked when the command function executed and the return of the function processed – all returned commands executed, all async tasks started, the model updated. But it invoked before the command go through "after" hubs.
Parameters
cmd
Command Command that was exectued and the return processedresult
any Returned object from the command's function
onEndExec
Invoked right after the command go throught "after" hubs. It is the final stage of command execution.
Parameters
cmd
Command Command that was fully executed and handled by parent blocks
onCatchError
Invoked if any uncaught error happened while execution of the command
or anywhere else in the logic, like in LogicBase#port or in LogicBase#computed.
By default print the error using console.error
.
Parameters
ComputedField
Class for declaring computed field with model dependencies. Given array shuold contain models, binded to some logec. They will be passed to compute function in the same order. Also they will be used to track changed and re-compute.
The class is immutable, so any call to any method will create
a new instance of ComputedField
and the old one won't be touched.
Parameters
Properties
deps
Array<Object> An array of models with binded logic (attached some Process)computeFn
Function A compute function that will be used to compute value of the computed field.
compute
Creates a new instance of the field and set compute function in it.
Parameters
func
Function A compute function that should return a value that will be used as a value for some computed field in a model. This function will be invoked with all dependency models as arguments
Examples
// A way to override computed field of some base logic class// (thanks to immutability of `ComputedField`) { const oldComputed = super; return ...oldComputed // Override `oldField` compute function with saved dependencies // and use overrided compute function inside new compute function oldField: oldComputed ; }
Returns ComputedField New instance of the ComputedField with computeFn
set to given function.
Mounter
To use mount function you need to implement a Mounter interface
which should have mount
and unmount
functions. It is up to the developer
how these functions will be implemented and what view library they will use.
There is a rule that mounter and view library should follow: view of a model should be updated only when the model updated and the update shouldn't touch views of children models (children views shouldn't be updated because their models is not changed).
React perfectly fits for this rule. By implementing shuoldComponentUpdate
you can control when the component should be updated and when shouldn't. In this
case this function should always return false
and componenent should be
updated using forceUpdate
only when the model is updated (using observe).
Properties
mount
Function A function that should expect two arguments: model object with attached Process instance and Block#View. It should render the model using given View (somehow).unmount
Function A function that shuold unmount mounted view from DOM.
Examples
// Minimal react mounter which follow the rulesComponent { const model = thisprops; thisstopObserver = } { this; } { return false; } { const View model = thisprops; const Logic = ; return <View model=model Logic=Logic /> } { return React; } { return React; }
defineCommand
Provides a way to define a command in the prototype without usign a decorator. You should give a prototype of the logic class, name of the function which should be converted to a command factory and the decorator function (optional).
If the decorator function is not provided, then cmd will be used by default.
Parameters
proto
Object Logic class prototypename
string Method name that you want to convert to a command factorydecorator
function? Optional decorator function
Examples
{ return field: thismodelfield + 1 ; } // throttle 100 { return this; } ;;
ensureCommand
Returns a Command instance or throw an error if given argument can't be used to create a Command (is not a Command or command factory).
Parameters
cmd
(Command | function) Command instance or command factory. When command factory then it will be invoked with no arguments to create a command
Examples
\@cmd { // do something } const logic = ; // returns Command instance// equals to...logic
Returns Command
decorateLogic
By given logic class go through prototypes chain and decorate all cmd functions. Use it if you can't use decorators in your project
It determine that the function should be converted to a command factory (decorated by cmd) by its name. If the function starts with uppercase letter or underscore+upper-letter, then it is considered as a command.
Parameters
LogicClass
LogicBase Logic class that you want to decoratedeep
bool If true then it will go though prototypes chain and decorate every prototype in the chain.
Examples
{ } { } { return 123; } // `SomeCommand` and `_SomePrivateCommand` will be decorated with `cmd`// (the prorotype will be changed in-place). `notACommand` won't// be decorated.; const logic = ;logic // returns Command instancelogic // returns Command instancelogic // returns 123
handleBefore
Function adds a handler to the Process attached to a given model that will be invoked before every command running for this model. The handler won't be invoked for commands from children models of given model.
Returns a function that removes the handler from the Process (stop handling)
Parameters
model
Object A model object with attached Processhandler
Function A function that will be called before every own command
Examples
; // Define root and child logic \@cmd {} { return childModel: ChildLogic ; } \@cmd {} // Run block with empty modelsconst res = ; // Adds a handler to a root model; // Run commands on root and child modelsresproc;resproc; // In the console will be printed only `RootCommand` command object// right before the command will be executed
Returns Function A function that stopps the handler
handleAfter
It is the same as handleBefore but the handler will be invoked after every own command.
Parameters
model
Object A model object with attached Processhandler
Function A function that will be called after every own command
Returns Function A function that stopps the handler
handle
Alias for handleAfter
Parameters
model
Object A model object with attached Processhandler
Function A function that will be called after every own command
Returns Function A function that stopps the handler
logicOf
Returns a Logic instance attached to a given model object. The logic instance stored in attached Process instance, so it execute procOf for the model to get a Process instance and then get the logic instance form a Process and return it.
If Process is not attached to the model, then an Error will be throwed. If the second argument passed then it will also check that the logic instance is instance of some particular class.
Parameters
model
Object A model with attached ProcesslogicClass
Class? Optional logic class to check that the logic instance is instance of the class
Returns LogicBase Returns a logic instance
observe
A function that adds a handler to the Process instance attached to a given model, that will be invoked on every command that changed the model. Useful for tracking changes to re-render a view of the model.
Parameters
model
Object A model with attached Process instancehandler
Function A function that will be invoked after every command that changed the model.options
Object? An object with optionsoptions.batched
bool If true then the handler will be invoked only when the commands stack is empty and model was changed during the stack execution. Useful to reduce amount of not necessary view re-renderings.
Examples
; \@cmd { return thisUpdateOne thisUpdateTwo ; } \@cmd { return one: thismodelone + 1 ; } \@cmd { return two: thismodeltwo + 1 ; } const res = ; ;; resproc; // `not-batched` will be printed two times// `batched` will be printed only once
Returns Function
procOf
Get a Process instance attached to the given model object.
Internally it get a __proc
field from the given model and returns it.
If given model do not have attached Process, then an Error will be throwed, but only if the second argument is not true, which ignores the error.
Parameters
model
Object A model with attached ProcessignoreError
bool If true then no error will be throwed if Process is not attached to the model
Returns Process An instance of Process attached to the model
bind
This function do the following:
- Creates a model object using Block#createModel. It is invoked without arguments.
- Create a Process instance with logic from Block#Logic
- Call Process#bind with created model
Returns an object with created process instance, model object and given block. Usefull when you want to prepare the block to run but you want to run it manually.
Parameters
block
Block A Block objectopts
Object? Options object that could change Process behaviour (optional, default{}
)opts.logger
DefaultLogger? A custom logger instance that will be used in the Process to track commands execution. By default DefaultLogger will be used.opts.shared
Object? An object that will be available in any logic in the tree asthis.shared
. Could be anything you want, but you will get more benifites if you will pass model object with attached Process as shared object to be able to make computed fields with depending of shared model.opts.args
Array<any>? An array of arguments that will be passed to LogicBase#config of the logic.opts.Process
Process? A class that will be used instead of Process. By default general Process is used, but you can customize it for your specific needs and pass here. Then every other process in the tree will be an instance of this custom Process implementation
Returns {proc: Process, model: Object, block: Block} An object with Process instance, model object and block that was passed to the function
hydrate
This function replace a createModel
function in given block
with new function that just returns a given model. It is useful
when you have some old model and just want to run everytihing with
it (for example for server rendering, or hot module replacement)
Parameters
Examples
// Hot module replacement example;; // Run initial blocklet res = ; // When block changed destroy the old process, hydrate a new// block with existing model and run hydrated new block againmodulehotaccept'./MyBlock' { resproc; const newBlock = ; res = ;};
Returns Block A new block with replaced createModel
function
which just returns a model that you passed as
second argument.
delay
A helper function for delaying execution. Returns a Promise which will be resolved in given amount of milliseconds. You can use it in task to implement some delay in execution, for debouncing for example.
Parameters
ms
number An amount of milliseconds to wait
Returns Promise A promise that resolved after given amount of milliseconds
Basic
The most common APIs that you will work with most of the time
Block
Block is a top level component in MangoJuice which consists of three parts:
- Model factory function
createModel
which should return a plain object. The main requirement for this function is that it should be able to return some model with zero arguments. But it can also accept some arguments as well. - A logic class which implements LogicBase interface
- A View function which is aimed to render the model created by
createModel
The View part is optional and it could have different type depending on selected View library to render a model and particular binding library.
Properties
Logic
LogicBase A logic class that should be attached to the model.createModel
Function A function that shuld return a plain object which will be an init model of the block.View
any? Something that some particular view library can render
Examples
// You can define block in short way using an objectconst MyBlock = field: 1 Logic: \@cmd { return field: thismodelfield + 1 ; } <div>modelfield</div> ; ;
// Or you can split it with modules (recommended)// ./MyBlock/index.js;;; View Logic createModel ;;; // ./MyBlock/Logic.js;; <Model> \@cmd { return field: thismodelfield + 1 ; } // ./MyBlock/Model.js;const createModel = field: 1; // ./MyBlock/View.jsconst MyBlockView = <div>modelfield</div>;; // ./index.js;; ;
LogicBase
Basic class for every logic. It is not necessaty to inherit an actual logic class from it, but for better type-checking it could be really usefull.
Logic class defines:
- A set of commands that could change an associated model – methods decorated with cmd
- What fields of the model should be associated with which logic class (children blocks) – LogicBase#children method
- A set of computed model fields – LogicBase#computed method
- A set of rules to handle commands from children logic – LogicBase#hubAfter and LogicBase#hubBefore methods
- A logic to handle external events – LogicBase#port method.
The logic class defines all the business-logic of some part of your application. The main goal of the logic – change associated model. Check cmd and Process#exec to have a better understanding how command can be defined and how it is executed.
Properties
model
Object A model object which is associated with the logic class in parent logic.meta
Object An object defined in LogicBase#config method for storing some internal logic state.shared
Object An object with some shared state, defined while run process of the app.
Examples
// ./blocks/MyBlock/Logic.js// @flow;;; <Model> { return child: ChildBlockLogic ; } { if cmd return this; } \@cmd { return field: arg + thismodelfield ; }
config
A function that defines init commands and meta object of the
logic. Can accept any type and number of arguments. Config
will take arguments passed to child function (for example
child(MyLogic, 1, 2, 3)
defines that the config
method of
MyLogic
will be invoked like config(1, 2, 3)
)
Could return an object with the fields:
initCommands
which could be an array or a single Command, that will be executed every time when the logic instance created and associated with some model (when Process#run invoked)meta
which could be an object with some internal state of the logic
Examples
{ return initCommands: thisStartThis this meta: something: propsamount ;}
children
This function defines what logic class should be associated with which model field. Should return an object where a key is a model field name, and a value is a logic class or an object that defines logic with arguments (return from child function)
Examples
{ return searchForm: searchResults: SearchResultsLogic ;}
Returns Object
computed
Should return an object which defines computed fields of the model. A key of the object is a model field, and value is a function or computed function definition object (return from depends function).
Computed field is lazy, which means that it is computed on the first use of the field and the computation result cached until the next update of the model.
If you want to define a computed field which ueses in computation not only fields from the own model, but maybe some values from child model, or from shared model, then you will need to use depends function to define what models used while computation to observe changes of these models to trigger an update event of the own model (to update views).
Examples
{ return thismodela + thismodelb withDeps: ;}
Returns Object
port
A function to handle global event, such as browser-level events,
websocket, intervals. The main porpouse of the port
is to subscribe
to some global events, execute appropriate commands from the logic
on the events and remove the handlers when the logic was destroyed.
Parameters
exec
Function It is a function that execute a passed commanddestroyed
Promise A promise that will be resolved when the logic destroyed
Examples
{ const timer = ; destroyed;}
hubAfter
Hub for all commands executed in the children blocks. The only
arguments is a command object that was executed.
Could return a Command or an array of Commands that should
be executed next.
Most of the time this function will look like a switch
,
but maybe with some additional rules for the cases. Take a look
to Command#is function to have a better understanding how you
can determine that the command is exactly what you are waiting for.
Parameters
cmd
Command Command that was executed in some child logic or in any child of the child and so on down to the leaf of the tree.
Examples
{ if cmd return this; if cmd return this thisAnotherHandler ; }
Returns (Command? | Array<Command>?)
hubBefore
Same as LogicBase#hubAfter, but catch commands before it will be actually executed, so you can compare prev and next model state for example.
Parameters
cmd
Command
Returns (Command? | Array<Command>?)
cmd
Decorator that converts a logic method to a command factory. The result function (command factory function) is a function that return a Command instnace with the decorated function and arguments that you will pass to a command factory function.
You can use this decorator without arguments or with an options argument (object)
Check Process#exec to see what the origin function could return to do something. If in short, it can return: nothing, command (or factory or array of commands), task, model update object.
Parameters
obj
Object If only this argument passed then it will be considered as options object which can customize command behaviour.obj.debounce
number If greeter than zero then an exeuction of the command will be debounced by given amount of millisecondsobj.throttle
number If greeter than zero then an exeuction of the command will be throttled by given amount of millisecondsobj.noInitCall
bool If true then debounced/throttled execution won't start with init command call. By default debounced/throttled command will be instantly exected and only then wait for a chance to execute again. WithnoInitCall=true
there is no instant init command call.obj.internal
bool Make the command invisible for parent LogicBase#hubBefore and LogicBase#hubAfter. Use it for commands that shouldn't be handled by hubs to increse performance.obj.name
string By default name of the command taken from origin function. With this option you can override the name with any other value.
methodName
descr
Examples
\@cmd { // do something }
\@ { // if you will call this command 3 times every // 100ms then it will be executed 2 times, // first right when you call it for a first time, // and second in 600ms. }
\@ { // if you will call this command 3 times every // 100ms then it will be executed 2 times, // first right when you call it for a first time, // and second in 300ms. }
\@ { // if you will call this command 3 times every // 100ms then it will be executed 1 time in 300ms. }
\@ { // this command can't be caught in `hubBefore` or `hubAfter` // in parent logics. Use it carefully. }
Returns (Function | object) Command factory function (if all three arguemnts passed) or a decorator function with binded options object (that you pass as a first argument)
run
Run given block. It is a short-hand function for running bind
and then running a Process by Process#run.
Also it returns an additional finished
Promise which is resolved
when all async tasks finished and all handler commands executed.
Parameters
Returns {proc: Process, model: Object, block: Block, finished: Promise} An object
which is almost the same as returned from bind, but with
additional finished
field, which is a Promise that will be resolved
when all blocks will execute all commands and all async tasks will be
finished.
mount
Mount running block. As third and next arguments you can pass other running blocks which will be stopped with the main running block (useful for HMR).
Parameters
mounter
Mounter An implementation of Mounter interfacerunRes
{proc: Process, block: Block, model: Object} An object that returned from run function.otherBlocks
...any
Examples
const MyBlock = field: 1 Logic: \@cmd { return field: thismodelfield + 1 ; } <div>modelfield</div> ; ;
Returns {view: any, stop: Function} An object with the result form Mounter#mount
function (in view
field) and stop function which destroy the process and
unmount a view using Mounter#unmount
task
Creates a TaskMeta object that could be returned from async task command. It describes the task that should be executed.
For more information what the Task is and how it should be implemented take a look to TaskMeta.
Parameters
taskFn
Function A function that could return a Promise
Properties
CANCEL
string A symbol that you can use to make a promise "cancellable"
Returns Object
cancel
This function should be used to cancel async task execution. Also it can cancel the debounced/throttled command.
- In case with task, if task was executing while you call a cancel command, the task will be cancelled (execution stopped). Otherwise nothing will happen.
- In case with debounced/throttled command, if the command scheduled to be executed then the schedule timer will be cleared and the command won't be executed. If the command is not scheduled then nothing happens.
To use it just pass a command or command factory as a first argument and return it from some other command.
Parameters
cmd
(Command | function) Command or command factory that you wan to cancel. Could be a command that returns task or debounced/throttled command
Examples
\@cmd { return ; } \@cmd { return ; } \@ { // do something }
Returns Command Returns a new command that will cancel the execution of the command passed as an argument.
child
Creates an object which describes child logic which will be attached to some model field. Should be used in LogicBase#children to define a logic that should be instantiated with some arguments passed to LogicBase#config.
Parameters
Examples
{ return meta: amount ; } { return modelField: // `config` of `ChildLogic` will be invoked with // `10` as a first argument }
Returns Object Object that contains a logic class and an arguments array that
should be used to invoke config
method of provided logic class.
depends
Function that helps to describe a computed field with external model dependncies. Should be used in LogicBase#computed. It creates an instance of ComputedField with dependencies passed as arguments.
A compute field with external dependencies is a field that should be updated (re-computed) not only when the own model udpated, but also when depdendency models updated.
Parameters
deps
...deps A list of models with attached logic
Examples
// some logic { return childModel: ChildLogic ; } { return // depends on `childModel` computedField: // depends on `shared` model computeWithShared: }
Returns ComputedField
Extras
Other pieces of MangoJuice
Process
The main class of MangoJuice that ties model, logic and view together.
It works in the following way:
- You create a model for your app (object) using
createModel
of the root block. The result is a plain object. - Then you need to create an instance of Process and pass logic class in the options object.
- Then you
bind
the Process instance to the model object you created on step one. "To bind Process to a model" means that in the model will be created a hidden field__proc
with a reference to the Process instance. - After that you can
run
the Process, which will execute init commands, port.
bind
and run
also look at the object returned from LogicBase#children
and create/run Process instances for children models.
Most of the time you do not need to care about all of these and just use run and mount. These small functions do everything described above for you.
Examples
; const ExampleBlock = { return a: 10 ; } Logic: \@cmd { return a: thismodela + amount ; } ; const model = ExampleBlock;const proc = logic: ExampleBlockLogic ;proc;proc;proc;
bind
Bind the process instance to a given model, which means that hidden
__proc
field will be created in the model with a reference to the
Process instance.
Also bind all children models – go through children definition returned by LogicBase#children, create Process for each child and bind it to an appropreat child model object.
Parameters
model
Object A model object that you want to bind to the Process.
run
Run the process – run children processes, then run port and init commands defined in config. Also run all model observers created by observe
destroy
Destroy the process with unbinding from the model and cleaning
up all the parts of the process (stop ports, computed fields).
__proc
field will be removed from the model object and all
children objects.
Parameters
deep
Boolean If true then all children blocks will be destroyed. By default, if not provided then considered as true.
exec
Exec a command in scope of the Process instance. It will use binded model and logic object to run the command. The command could be function (command factory) or object (Command instance).
A command origin function could return three types of things:
- Another Command/command factory or an array of commands (an element of the array also could be another array with commands or null/undefined/false). All commands will be execute in provided order.
- An instance of TaskMeta (which is usually created by task helper function). The returned task will be started and do not block execution of next commands in the stack.
- A plain object which is a model update object. The object will be merged with current model.
If command is undefined or Process
instance not binded
to any model it will do nothing.
Parameters
Examples
\@cmd { return 1 > 2 && thisSomeCoomand this 2 > 1 && thisAndSomeOtherCommand this ; } \@cmd { return } \@cmd { if Math > 06 return field: Date ; }
finished
Returns a promise which will be resolved when all async tasks and related handlers in the process and all children processes will be finished.
Returns Promise
Command
Class which declares a command that will be executed in the future by some Process#exec. It contains a function that will be executed, arguments that should be passed to the function and command name.
The command also keep a context that should be used to execute a function. Usually this context is some logic instance. You can bind a context to the command using Command#bind function.
The command instance is immutable, so any function that makes some change in the command will produce a new command instead of changing the original one.
Parameters
Properties
func
Function An origin function that should be executedargs
Array<any> A set of arguments that should be used to execute the fucntion.
exec
Execute a function stored in the command instance. It passes stored arguments to the function and call it in stored context. Returns the value that was returned form the function.
Returns any
clone
Clone instance of the command
Returns Command
is
Check is the command equal to some other command or have a reference to the same origin function. Optionally you can also check that the command binded to some concrete model (using the second argument).
You can use many different formats to define the command to check:
- Just a string, which should be constructed like
Logic Class Name + Function Name
- Command object or command factory that you can get from prorotype of some logic class.
- Binded command object or command factory that you can get from concrete model using logicOf function
Parameters
cmd
(Command | string) Command object or command factory or command namechildModel
Object? Model object that should be binded to the command (optional)
Examples
\@cmd {} { if cmd // by command name if cmd // by command factory if cmd // by binded to a concrete model command factory (commands // binded for other models will be ignored) if cmd // by command factory and concrete child model (commands // binded for other models will be ignored) }
Returns Boolean Returns true
if the command have same name, or same origin function
and binded to same model (if second argument provided)
appendArgs
Creates a new Command instance (clone the command) and append given list of arguments to the current list of arguments. Returns a new Command with new list of arguments.
Parameters
args
Array<any> List of arguments that will be appened to tne new Command
Returns Command New command with appended arguments
bind
Creates a new Command instance and set given logic instance in it. Also update command name by name of the origin function and name of the logic class.
Parameters
logic
LogicBase A logic instance that should be binded to a command
Returns Command New command binded to given logic instance
TaskMeta
Class for declaring an async task. An instance of this object returned from taks function.
A task function is a function that could return a Promise. The resolved value will be passed to a success command handler, the rejected command will be passed to a fail command handler.
If task function do not return a Promise, then the returned value passed to a success command, and if the function throw an error then the error passed to a fail command.
The task function receive at least one argument – an object with model
, shared
and meta
. All the next arguments will be given from a command that returned a task,
if it is not overrided by TaskMeta#args
By default a task is single-threaded. It means that every call to a task will cancel previously running task if it is running. You can make the task to be multi-threaded by TaskMeta#multithread, so every call to a task won't cancel the running one.
In a context of the task function defined special call
function. This function should
be used to invoke sub-tasks. It is important to use call
to run sub-tasks because
call
create a cancellation point of the task – if the task cancelled, then nothing
will be executed after currently running call
sub-task.
call
returns an object with result
and error
fields. If error
field is not
empty, then something weird happened in the sub-task. call
internally have a try/catch,
so it is not necessary to wrap it with try/catch, just check the error
field in the
returned object and if it is not empty – handle it somehow (throw it, or do any custom
error handling)
Also in a context of the task defined a notify
function. It is a function that execute
notify command if it is defined. The command executed with the same arguments as passed
to notify
function. You can use it to incrementally update a model while the long
task is executing, to show some progress for the user.
Parameters
Examples
{ this; await this; return a / b * c;} { const result error = await this; if error throw 'Something weird happened in subtask' return result + 10;}
\@cmd { return }
notify
Set a notify handler command. This command executed by call of this.notify
inside a task with the same arguments as passed to this.notify
Parameters
cmd
Command
Returns TaskMeta
success
Set a success handler command. Will be executed with a value returned from the task, or if the task returned a Promise – with resovled value.
Parameters
cmd
Command
Returns TaskMeta
fail
Set a fail handler command. Will be executed with error throwed in the task, or if the task returned a Promise – with rejected value.
Parameters
cmd
Command
Returns TaskMeta
multithread
Define the task to be "multi-thread", so every call will run in parallel with other calls of the same task in scope of one process (model).
Parameters
val
boolean
Returns TaskMeta
engine
Set task executor function. The executor function should return an object
that should have at least two fields: exec
and cancel
functions.
exec
should return a promise which should be resolved or rejected
with an object { result, error }
. A cancel
function should stop
the task execution.
Parameters
engine
Function A function that returns{ exec: Function, cancel: Function }
object
Returns TaskMeta
args
Override arguments which will be passed to the task starting from second argument. By default a task will receive the same set of arguments as a task command. If this function invoked, then the task will receive given arguments instead of command arguments.
Parameters
args
...any
Returns TaskMeta
DefaultLogger
A class which defins a logger for tracking command execution process. The default implmenetation just defines all possible logger methods (log points) and print errors to the console. Extend this class to define your own logging functionality (like logging errors with Setnry or Loggly)
Examples
// ./index.js;; // Implement custom logger { Raven; } // Pass instance of the Logger to `run` options object.;
onStartExec
This method invoked right before anything else - as the first thing in Process#exec. Even before any LogicBase#hubBefore.
Parameters
cmd
Command Command that just started execution process
onStartHandling
Invoked right before command will go through all LogicBase#hubBefore or LogicBase#hubAfter up in the blocks tree. The second argument indicates is it called for LogicBase#hubBefore or for LogicBase#hubAfter.
Parameters
cmd
Command Command that is going to go through hubs in parent blocksisAfter
Boolean If true then the command is going through "after" hubs, "before" otherwise
onEndHandling
Invoked when the command went through all hubs and when all sync commands returned from hubs was executed.
Parameters
cmd
Command Command that went through all hubsisAfter
Boolean If true then the command going through "after" hubs, "before" otherwise
onExecuted
Invoked when the command function executed and the return of the function processed – all returned commands executed, all async tasks started, the model updated. But it invoked before the command go through "after" hubs.
Parameters
cmd
Command Command that was exectued and the return processedresult
any Returned object from the command's function
onEndExec
Invoked right after the command go throught "after" hubs. It is the final stage of command execution.
Parameters
cmd
Command Command that was fully executed and handled by parent blocks
onCatchError
Invoked if any uncaught error happened while execution of the command
or anywhere else in the logic, like in LogicBase#port or in LogicBase#computed.
By default print the error using console.error
.
Parameters
ComputedField
Class for declaring computed field with model dependencies. Given array shuold contain models, binded to some logec. They will be passed to compute function in the same order. Also they will be used to track changed and re-compute.
The class is immutable, so any call to any method will create
a new instance of ComputedField
and the old one won't be touched.
Parameters
Properties
deps
Array<Object> An array of models with binded logic (attached some Process)computeFn
Function A compute function that will be used to compute value of the computed field.
compute
Creates a new instance of the field and set compute function in it.
Parameters
func
Function A compute function that should return a value that will be used as a value for some computed field in a model. This function will be invoked with all dependency models as arguments
Examples
// A way to override computed field of some base logic class// (thanks to immutability of `ComputedField`) { const oldComputed = super; return ...oldComputed // Override `oldField` compute function with saved dependencies // and use overrided compute function inside new compute function oldField: oldComputed ; }
Returns ComputedField New instance of the ComputedField with computeFn
set to given function.
Mounter
To use mount function you need to implement a Mounter interface
which should have mount
and unmount
functions. It is up to the developer
how these functions will be implemented and what view library they will use.
There is a rule that mounter and view library should follow: view of a model should be updated only when the model updated and the update shouldn't touch views of children models (children views shouldn't be updated because their models is not changed).
React perfectly fits for this rule. By implementing shuoldComponentUpdate
you can control when the component should be updated and when shouldn't. In this
case this function should always return false
and componenent should be
updated using forceUpdate
only when the model is updated (using observe).
Properties
mount
Function A function that should expect two arguments: model object with attached Process instance and Block#View. It should render the model using given View (somehow).unmount
Function A function that shuold unmount mounted view from DOM.
Examples
// Minimal react mounter which follow the rulesComponent { const model = thisprops; thisstopObserver = } { this; } { return false; } { const View model = thisprops; const Logic = ; return <View model=model Logic=Logic /> } { return React; } { return React; }
defineCommand
Provides a way to define a command in the prototype without usign a decorator. You should give a prototype of the logic class, name of the function which should be converted to a command factory and the decorator function (optional).
If the decorator function is not provided, then cmd will be used by default.
Parameters
proto
Object Logic class prototypename
string Method name that you want to convert to a command factorydecorator
function? Optional decorator function
Examples
{ return field: thismodelfield + 1 ; } // throttle 100 { return this; } ;;
ensureCommand
Returns a Command instance or throw an error if given argument can't be used to create a Command (is not a Command or command factory).
Parameters
cmd
(Command | function) Command instance or command factory. When command factory then it will be invoked with no arguments to create a command
Examples
\@cmd { // do something } const logic = ; // returns Command instance// equals to...logic
Returns Command
decorateLogic
By given logic class go through prototypes chain and decorate all cmd functions. Use it if you can't use decorators in your project
It determine that the function should be converted to a command factory (decorated by cmd) by its name. If the function starts with uppercase letter or underscore+upper-letter, then it is considered as a command.
Parameters
LogicClass
LogicBase Logic class that you want to decoratedeep
bool If true then it will go though prototypes chain and decorate every prototype in the chain.
Examples
{ } { } { return 123; } // `SomeCommand` and `_SomePrivateCommand` will be decorated with `cmd`// (the prorotype will be changed in-place). `notACommand` won't// be decorated.; const logic = ;logic // returns Command instancelogic // returns Command instancelogic // returns 123
handleBefore
Function adds a handler to the Process attached to a given model that will be invoked before every command running for this model. The handler won't be invoked for commands from children models of given model.
Returns a function that removes the handler from the Process (stop handling)
Parameters
model
Object A model object with attached Processhandler
Function A function that will be called before every own command
Examples
; // Define root and child logic \@cmd {} { return childModel: ChildLogic ; } \@cmd {} // Run block with empty modelsconst res = ; // Adds a handler to a root model; // Run commands on root and child modelsresproc;resproc; // In the console will be printed only `RootCommand` command object// right before the command will be executed
Returns Function A function that stopps the handler
handleAfter
It is the same as handleBefore but the handler will be invoked after every own command.
Parameters
model
Object A model object with attached Processhandler
Function A function that will be called after every own command
Returns Function A function that stopps the handler
handle
Alias for handleAfter
Parameters
model
Object A model object with attached Processhandler
Function A function that will be called after every own command
Returns Function A function that stopps the handler
logicOf
Returns a Logic instance attached to a given model object. The logic instance stored in attached Process instance, so it execute procOf for the model to get a Process instance and then get the logic instance form a Process and return it.
If Process is not attached to the model, then an Error will be throwed. If the second argument passed then it will also check that the logic instance is instance of some particular class.
Parameters
model
Object A model with attached ProcesslogicClass
Class? Optional logic class to check that the logic instance is instance of the class
Returns LogicBase Returns a logic instance
observe
A function that adds a handler to the Process instance attached to a given model, that will be invoked on every command that changed the model. Useful for tracking changes to re-render a view of the model.
Parameters
model
Object A model with attached Process instancehandler
Function A function that will be invoked after every command that changed the model.options
Object? An object with optionsoptions.batched
bool If true then the handler will be invoked only when the commands stack is empty and model was changed during the stack execution. Useful to reduce amount of not necessary view re-renderings.
Examples
; \@cmd { return thisUpdateOne thisUpdateTwo ; } \@cmd { return one: thismodelone + 1 ; } \@cmd { return two: thismodeltwo + 1 ; } const res = ; ;; resproc; // `not-batched` will be printed two times// `batched` will be printed only once
Returns Function
procOf
Get a Process instance attached to the given model object.
Internally it get a __proc
field from the given model and returns it.
If given model do not have attached Process, then an Error will be throwed, but only if the second argument is not true, which ignores the error.
Parameters
model
Object A model with attached ProcessignoreError
bool If true then no error will be throwed if Process is not attached to the model
Returns Process An instance of Process attached to the model
bind
This function do the following:
- Creates a model object using Block#createModel. It is invoked without arguments.
- Create a Process instance with logic from Block#Logic
- Call Process#bind with created model
Returns an object with created process instance, model object and given block. Usefull when you want to prepare the block to run but you want to run it manually.
Parameters
block
Block A Block objectopts
Object? Options object that could change Process behaviour (optional, default{}
)opts.logger
DefaultLogger? A custom logger instance that will be used in the Process to track commands execution. By default DefaultLogger will be used.opts.shared
Object? An object that will be available in any logic in the tree asthis.shared
. Could be anything you want, but you will get more benifites if you will pass model object with attached Process as shared object to be able to make computed fields with depending of shared model.opts.args
Array<any>? An array of arguments that will be passed to LogicBase#config of the logic.opts.Process
Process? A class that will be used instead of Process. By default general Process is used, but you can customize it for your specific needs and pass here. Then every other process in the tree will be an instance of this custom Process implementation
Returns {proc: Process, model: Object, block: Block} An object with Process instance, model object and block that was passed to the function
hydrate
This function replace a createModel
function in given block
with new function that just returns a given model. It is useful
when you have some old model and just want to run everytihing with
it (for example for server rendering, or hot module replacement)
Parameters
Examples
// Hot module replacement example;; // Run initial blocklet res = ; // When block changed destroy the old process, hydrate a new// block with existing model and run hydrated new block againmodulehotaccept'./MyBlock' { resproc; const newBlock = ; res = ;};
Returns Block A new block with replaced createModel
function
which just returns a model that you passed as
second argument.
delay
A helper function for delaying execution. Returns a Promise which will be resolved in given amount of milliseconds. You can use it in task to implement some delay in execution, for debouncing for example.
Parameters
ms
number An amount of milliseconds to wait
Returns Promise A promise that resolved after given amount of milliseconds