Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

function-tree

Package Overview
Dependencies
Maintainers
5
Versions
487
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

function-tree

When a function is not enough

  • 1.0.2
  • Source
  • npm
  • Socket score

Version published
Maintainers
5
Created
Source

function-tree

Install

NPM

npm install function-tree

Description

Function-tree is the what Cerebral extends to create its signal implementation. You can use this library on the server, or standalone in the client as a replacement for Cerebral. Basically a function-tree allows you to execute a tree of functions. You can use the Cerebral debugger to debug function tree execution in any JS environment.

Function-tree is somewhat in the same family as Rxjs and Promises. The main difference is that Rxjs and Promises are based on value transformation. That means only the value returned from the previous function is available in the next. This works when you indeed want to transform values, but events in your application are rarely about value transformation, they are about running side effects and going through one of multiple execution paths. Function tree embraces the fact that most of what we do in application development is running side effects.

Rxjs and Promises are also about execution control, but neither of them have declarative conditional execution paths, you have to write an IF or SWITCH statement or decouple streams. With function tree you are able to diverge the execution down paths just as declaratively as functions. This helps readability.

Instantiate

import FunctionTree from 'function-tree'
// Node:
// const FunctionTree = require('function-tree').FunctionTree

const ft = new FunctionTree({
  // add side effect libraries to context
})

ft.run([
// returns a promise
  function someFunc (context) {},
  function someOtherFunc (context) {}
], {
  foo: 'bar' // optional payload
})
.catch((error) => {
  // Current payload with execution details,
  // can be passed in to a new execution (will be indicated in debugger)
  error.payload

  // A serialized version of the error. Name, message and stack, or custom error serialization
  error.payload.error
})

You can also add multiple custom context providers by using an array:

const ft = new FunctionTree([{
    // add side effect libraries to context
  },
  SomeCustomProvider()
])

errors

FunctionTreeError (base)

import {FunctionTreeError} from 'function-tree'
// Node:
// const FunctionTreeError = require('function-tree').FunctionTreeError

// Error structure
{
  name: 'FunctionTreeError',
  message: 'Some function-tree error'
  stack: '...'  
}

FunctionTreeExecutionError

import {FunctionTreeExecutionError} from 'function-tree'
// Node:
// const FunctionTreeExecutionError = require('function-tree').FunctionTreeExecutionError

// Error structure
{
  name: 'FunctionTreeExecutionError',
  message: 'Some execution error'
  execution: {name: 'someName'},
  funcDetails: {name: 'someFunction', functionIndex: 5},
  payload: {foo: 'bar'},
  stack: '...'  
}

devtools

Download the function tree standalone debugger for Mac, Windows or Linux.

import FunctionTree from 'function-tree'
import Devtools from 'function-tree/devtools'
// Node:
// const FunctionTree = require('function-tree').FunctionTree
// const Devtools = require('function-tree/devtools').Devtools

const devtools = new Devtools({
  // Set url of remote debugger
  host: 'localhost:8585',

  // By default debugger tries to reconnect when it is not active
  reconnect: true
})
const ft = new FunctionTree([])

// Add your function tree to the debugger
devtools.add(ft)

// If you are not going to use it anymore, remove it
devtools.remove(ft)

// Remove all function trees from debugger
devtools.destroy()

You can use it when creating providers to easily wrap their usage:

function MyProvider (options = {}) {
  let cachedProvider = null

  function createProvider (context) {
    return {
      doSomething() {},
      doSomethingElse() {}
    }
  }

  return (context) => {
    context.myProvider = cachedProvider = (cachedProvider || createProvider(context))

    if (context.debugger) {
      context.debugger.wrapProvider('myProvider')
    }

    return context
  }
}

sequence

You can use an array literal to define a sequence of functions.

function someFunction (context) {}
function someOtherFunction (context) {}

module.exports = [
  someFunction,
  someOtherFunction
]

Or you can be explicit by using the sequence function:

import {sequence} from 'function-tree'
// Node:
// const sequence = require('function-tree').sequence

function someFunction (context) {}
function someOtherFunction (context) {}

module.exports = sequence([
  someFunction,
  someOtherFunction
])

The first argument to sequence can be a string, which names the sequence. This will be shown in the debugger. If it is the root sequence it will be used as the name of the execution itself.

import {sequence} from 'function-tree'
// Node:
// const sequence = require('function-tree').sequence

function someFunction (context) {}
function someOtherFunction (context) {}

module.exports = sequence('My awesome sequence', [
  someFunction,
  someOtherFunction
])

parallel

import {parallel} from 'function-tree'
// Node:
// const sequence = require('function-tree').parallel

function someFunction (context) {}
function someOtherFunction (context) {}

module.exports = parallel([
  someFunction,
  someOtherFunction
])

Even though someFunction returns a Promise, someOtherFunction will be run immediately.

context

props
import FunctionTree from 'function-tree'
// Node:
// const FunctionTree = require('function-tree').FunctionTree

function funcA (context) {
  context.props.foo // "bar"
}

const ft = new FunctionTree()
const tree = [
  funcA
]

ft.run(tree, {foo: 'bar'})
path

The path is only available on the context when the function can diverge the execution down a path.

import FunctionTree from 'function-tree'
// Node:
// const FunctionTree = require('function-tree').FunctionTree

function funcA (context) {
  context.props.foo // "bar"

  return context.path.pathA({foo2: 'bar2'})
}

function funcB (context) {
  context.props.foo // "bar"
  context.props.foo2 // "bar2"

  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({foo3: 'bar3'})
    }, 100)
  })
}

function funcC(context) {
  context.props.foo // "bar"
  context.props.foo2 // "bar2"
  context.props.foo3 // "bar3"
}

const ft = new FunctionTree([])
const tree = [
  funcA, {
    pathA: [
      funcB,
      funcC
    ],
    pathB: []
  }
]

ft.run(tree, {foo: 'bar'})

error

import FunctionTree from 'function-tree'
// Node:
// const FunctionTree = require('function-tree').FunctionTree
const ft = new FunctionTree([])

// As an event (async)
ft.on('error', function (error, execution, payload) {})

// As callback for single execution
// Triggers sync/async depending on where error occurs
ft.run(tree, (error) => {})

// As callback (sync)
ft.run(tree, (error, execution, payload) => {
  if (error) {
    // There is an error
  }
})

provider

A provider gives you access to the current context and other information about the execution. It is required that you return the context or a mutated version of it.

import FunctionTree from 'function-tree'
// Node:
// const FunctionTree = require('function-tree').FunctionTree

function MyProvider(context, functionDetails, payload) {
  context // Current context
  context.props // Input created by the PropsProvider (default)

  payload // The current payload (Used by PropsProvider)

  functionDetails.name // Name of the function
  functionDetails.functionIndex // The index of the function in the tree, like an ID
  functionDetails.function // A reference to the running function
  functionDetails.isParallel // If the function is running in parallel with others

  context.execution.name // Function tree id
  context.execution.id // Current execution id
  context.execution.staticTree // The static representation of the tree
  context.execution.datetime // Time of execution
  context.execution.functionTree // The function tree instance

  return context // Always return the changed context
}

const ft = new FunctionTree([
  MyProvider
])

Providers lets us do some pretty amazing things. The debugger for function-tree is actually just a provider that sends information to the debugger about execution and exposes an API for other providers to send their own data to the debugger.

StopExecutionProvider

By using a condition you can tell the function tree to stop executions.

import {StopExecutionProvider} from 'function-tree'
// Node:
// const StopExecutionProvider = require('function-tree').StopExecutionProvider

const ft = new FunctionTree([
  StopExecutionProvider({
    nameOfExecution (context) {
      return context.props.stop
    }
  })
])

If the condition returns true the execution will throw an error and stop.

events

The execute function is also an event emitter.

import FunctionTree from 'function-tree'
// Node:
// const FunctionTree = require('function-tree').FunctionTree

const ft = new FunctionTree([])
const tree = [
  funcA
]

// When an error is thrown, also catches promise errors
ft.on('error', (error, execution, payload) => {})

// When a function tree is executed
ft.on('start', (execution, payload) => {})

// When a function tree execution has ended
ft.on('end', (execution, payload) => {})

// When a function tree goes down a path
ft.on('pathStart', (execution, payload) => {})

// When a function tree ends execution of a path
ft.on('pathEnd', (execution, payload) => {})

// When a function in a function tree starts executing
ft.on('functionStart', (execution, functionDetails, payload) => {})

// When a function in a function tree stops executing
ft.on('functionEnd', (execution, functionDetails, payload) => {})

// Triggers when an async function has been run
ft.on('asyncFunction', (execution, functionDetails, payload) => {})

// When a parallel execution is about to happen (array in array)
ft.on('parallelStart', (execution, payload, functionsToResolveCount) => {})

// When a function in parallel execution is done executing
ft.on('parallelProgress', (execution, payload, functionsStillResolvingCount) => {})

// When a parallel execution is done
ft.on('parallelEnd', (execution, payload, functionsExecutedCount) => {})

ft.run(tree)

Keywords

FAQs

Package last updated on 09 Sep 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc