Code Monkey home page Code Monkey logo

yau's Introduction

YetAnotherUtility

YAU provides a few methods and a style guide to write asynchronous, possibly distributed code under the actor model with asynchronous events, similar to the Akka framework.

Xemitter

xemitter uses sindresorhus/emittery to provide an event emitter and task delegation facility that simplifies building asynchronous applications using the Actor pattern.

Events are pairs of channel names and arbitrary data items. Events are emitted by emitter functions.

An arbitrary number of listeners can listen on any given channel. Listeners may be synchronous (returning anything but a promise) or asynchronous (returning a promise). Each listener produces a value, be it implicitly (undefined) or explicitly (by using return x or resolve x). The outcomes of all listeners are collected into an array of values, which may or may not be consumed by emitters.

Delegators are (inherently asynchronous) emitter functions that not only emit events, but that also use the result(s) that the event listener(s), if any, produced.

Because of the inherent unpredictability of the asynchronous mode of operation, no guarantee is made about the ordering of values in the event result array. Since an important use case for event emitting is task delegation, there is a way to distinguish a primary result from spurious and secondary results: On the one hand, up to one listener may bind to a channel using XMT.primary_on. Whatever values(s) that listener produces when answering an event will be wrapped into a nonce object. The delegator then uses await XMT.delegate or XMT.select await XMT.emit to retrieve up to one primary item from the event results:

# define a function that delegates some task:
sample_delegator = ->
result = await delegate 'some_task', 42
if is_sad result
  ... sad path ...
else
  ... happy path ...
  return some_value

# use the delegator:
sample_delegator()
.then ( x ) ->
  return error_handler x if is_sad x
  # xxx
  help 'resolved', jr x
.catch error_handler
# xemitter

#-----------------------------------------------------------------------------------------------------------
@_emitter = new Emittery()
@_has_primary_listeners = {}

#-----------------------------------------------------------------------------------------------------------
@_mark_as_primary = ( x ) -> { '~isa': 'XEMITTER/preferred', value: x, }

#-----------------------------------------------------------------------------------------------------------
@select = ( values ) -> ( values.filter ( x ) -> CND.isa x, 'XEMITTER/preferred' )[ 0 ]?.value ? null

#-----------------------------------------------------------------------------------------------------------
@primary_on = ( channel, listener ) ->
    if @_has_primary_listeners[ channel ]
      throw new Error "channel #{rpr channel} already has a primary listener"
    @_has_primary_listeners[ channel ] = yes
    @_emitter.on channel, ( data ) =>
      return @_mark_as_primary await listener data

#-----------------------------------------------------------------------------------------------------------
@also_on = ( channel, listener ) ->
    @_emitter.on channel, listener

#-----------------------------------------------------------------------------------------------------------
@emit     = ( channel, data ) ->               @_emitter.emit channel, data
@delegate = ( channel, data ) -> @select await @_emitter.emit channel, data

# debug '22621', Object::toString.call @delegate

############################################################################################################
for name, value of L = @
    ### TAINT poor man's 'callable' detection ###
    continue unless CND.isa_function value.bind
    L[ name ] = value.bind L
{ select, emit, delegate, also_on, primary_on, } = require 'xemitter'

#-----------------------------------------------------------------------------------------------------------
error_handler = ( reason ) ->
  if is_sad reason
    return urge 'this is sad:', jr reason
  if is_crash reason
    warn CND.reverse 'bad'
    warn 'this is a crash'
    warn jr reason
    process.exit 1
  # don't throw new Error( reason );
  # throw reason
  alert CND.reverse 'evil'
  alert reason
  return null

#-----------------------------------------------------------------------------------------------------------
also_on 'some_task_A', ( data ) ->
  debug 'on some_task_A', jr data
  return 'a secondary result'

#-----------------------------------------------------------------------------------------------------------
also_on 'some_task_B', ( data ) ->
  debug 'on some_task_B', jr data
  return 'a secondary result'

Example A

# use_sample_delegator_A

#-----------------------------------------------------------------------------------------------------------
primary_on 'some_task_A', ( data ) ->
debug 'on some_task_A', jr data
return new Promise ( pass, toss ) ->
  if Math.random() > 0.5
    pass "a happy primary result"
  else
    pass new_failure 'code42', "a sad primary result"
  return null

#-----------------------------------------------------------------------------------------------------------
sample_delegator_A = ->
result = await delegate 'some_task_A', 42
# result = select await emit 'some_task_A', 42
if is_sad result
  urge 'sample_delegator_A sad result:    ', jr result
  return null
else
  help 'sample_delegator_A happy result:  ', jr result
  return "**#{result}**"

#-----------------------------------------------------------------------------------------------------------
sample_delegator_A()
.then ( x ) ->
  return error_handler x if is_sad x
  # xxx
  help 'resolved', jr x
.catch error_handler

Example B

# use_sample_delegator_B

#===========================================================================================================
### Synchronous contractors without promises and asynchronous contractors with promises show the same
behavior; crucially, **the delegator does not have to be aware of any difference between the two**: ###
if settings.use_promises_in_contractor
  info "using contractor with promises"
  #-----------------------------------------------------------------------------------------------------------
  primary_on 'some_task_B', ( data ) ->
    debug 'on some_task_B', jr data
    return new Promise ( resolve, reject ) ->
      [ a, b, ] = data
      return reject new_failure 'divbyzero', "division by zero: #{rpr a} / #{rpr b}", null if b is 0
      resolve a / b
else
  info "using contractor *without* promises"

  #-----------------------------------------------------------------------------------------------------------
  primary_on 'some_task_B', ( data ) ->
    debug 'on some_task_B', jr data
    [ a, b, ] = data
    throw new_failure 'divbyzero', "division by zero: #{rpr a} / #{rpr b}", null if b is 0
    return a / b

#-----------------------------------------------------------------------------------------------------------
sample_delegator_B = ->
  try
    #.......................................................................................................
    info "computing 4 / 5"
    result_1 = await delegate 'some_task_B', [ 4, 5, ]
    info "computing 4 / 5: #{result_1}"
    #.......................................................................................................
    info "computing 3 / 0"
    result_2 = await delegate 'some_task_B', [ 3, 0, ]
    info "computing 3 / 0: #{result_2}"
    #.......................................................................................................
    # In the case of a style B contractor, only happy results are resolved; sad and bad results are
    # rejected and end up in the catch clause:
    return [ result_1, result_2, ]
  catch unhappy
    warn '28921', unhappy
    if is_sad unhappy
      # deal with failures: possibly log where and what occurred, return a replacement value (that may in
      # itself by happy or sad):
      result_2 = happy unhappy
      urge "computing 3 / 0: #{result_2}"
      urge 'sample_delegator_B sad result:    ', jr unhappy
      return null
    # refuse to deal with anything else:
    throw unhappy

#-----------------------------------------------------------------------------------------------------------
sample_delegator_B()
  .then ( x ) ->
    return error_handler x if is_sad x
    # xxx
    help 'resolved', jr x
  .catch error_handler
use_sample_delegator_A()
use_sample_delegator_B { use_promises_in_contractor: yes, }
use_sample_delegator_B { use_promises_in_contractor: no, }

Synchronous contractors without promises and asynchronous contractors with promises show the same behavior; crucially, the delegator does not have to be aware of any difference between the two

@emit     = ( channel, data ) ->                                await @_emitter.emit channel, data
@entrust  = ( channel, data ) ->                  @_get_primary await @_emitter.emit channel, data
@delegate = ( channel, data ) -> @reject_unhappy  @_get_primary await @_emitter.emit channel, data

### TAINT must consider case where no listener is available which leads to defect or a crash

  • emit: Send out some (optional) data on a named channel, await all the results.
  • entrust: Send out some (optional) data on a named channel, await the primary result or null if no-one listened; where a contractor returns or resolves to an unhappy result, handling is up to the delegator.
  • delegate: Send out some (optional) data on a named channel, await the primary result or null if no-one listened; where a contractor returns or resolves to an unhappy result, an exception is thrown so that all unhappy outcomes have to be dealt with either in a catch block (or in the .catch() method).

Links

yau's People

Contributors

loveencounterflow avatar

Watchers

James Cloos avatar  avatar  avatar

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.