async-endpoint

0.0.23 • Public • Published

Async Endpoint

Async Endpoint Logo

Asynchronous Endpoints, especially when paired with a functional style of programming, provide a sound method of writing programs in JavaScripts that are

- testable
- maintainable
- extendable
- composeable
- easy to reason about
- standard based*

This repository provides a brief intoduction to asynchronous endpoints**, along with a helper library async-endpoint to help make a few things easier.

Table of contents

Introduction

Many programming languages use the concept of functions as entry points to transfer control between programs.

Synchronous Endpoints

In javascript, this is as simple as writing a function and calling it:

Example 1

const program = function() {
  console.log("hello world");
};

program();
//logs "hello world"

Taking a queue from functional programming, we can remove the logging side affect from the main program into a separate render function that logs the result retuned from running:

Example 2

const render = console.log;

const program = function() {
  return "hello world";
};

render(program());
//logs "hello world"

Synchronous Iteration

Provided that our render function expects an iterator, We can construct our program with a generator function to yeild multiple results:

Example 3

const render = iterator => {
  //as the result is an iterator
  //we iterate though it and log each subsequent result
  for (const result of iterator) {
    console.log(result);
  }
};

const program = function*() {
  yield "hello";
  yield "world";
};

render(program());
//logs "hello"
//logs "world"

Asynchronous Iteration

When using asynchronous generators, we can await asynchronous APIs, though we must again make sure to modify our fetch function.

Example 4

const render = async asynchronousIterator => {
  //we use the "for await" construct for Asynchronous Iterators
  for await (const result of asynchronousIterator) {
    console.log(result);
  }
};

const program = async function*() {
  yield "fetching...";
  yield await fetch("https://www.google.com");
  yield "results fetched.";
};
render(program());
//logs "fetching..."
//logs result from fetch.
//logs "results fetched."

Asynchronous Input and Interactive Programs

With a few small tricks, asynchronous generators can function as fully interactive programs.

We'll take advantage of the included AsyncArray class to derive a "request" and a "respond" function.

When a program calls request, it will return a promise. This promise will then be fulfilled with the input of the next call to respond.

import { AsyncArray } from "async-endpoint";
const channel = new AsyncArray();
const respond = channel.push.bind(channel),
  request = async ()=>(await channel.next()).value;

By convention, we'll pass two arguments to our asynchronous generator function:

- an _init_ object, which may or may not be ignored
- the aforementioned _request_ function
const program = async function *(init, request){
    ...
}

Finally, we need to connect the respond object to user input. If running in a browser, you could simply attach it to the window object:

window.respond = respond;

Or, if running in node, you can use the included inputConsole function:

import { inputConsole } from "async-endpoint";
inputConsole(respond);

Puttig it all together, we can write an interactive program like this: (Note that instead of defining a render function, we're using the generic renderer from the async-endpoit library instead of writing our own this time).

Example 5

import { AsyncArray, inputConsole, renderer } from "async-endpoint";

const render = renderer();

const program = async function*(init, request) {
  yield "What's your name?";
  yield `Hello ${await request()}`;
};

const channel = new AsyncArray();
const respond = channel.push.bind(channel),
  request = async ()=>(await channel.next()).value;

inputConsole(respond);

render(program(undefined, request)); //the init object will be ignored
//logs "

Related

Renderer for React

Import

There are a few ways to import this into your project.

Pre-Ecmascript Modules

Common JS

The package's main file is a compiled common JS file.

const AsyncEndpoint = require("async-endpoint");
const AsyncEndpoint = require("async-endpoint/common.js"); //also works

This should work with file bundlers, though I haven't had a chance to test it.

import * as AsyncEndpoint from = "async-endpoint/common.js";

Browser

There is also a rollup of the package for the browser, though there are a number of issues getting this to work out of the box. You're probably better off pointing directly to the files in the "js" or "mjs" folders and using a bundler.

    <script src = ".../async-endpoint/browser.js"></script>
    <script>
        alert(typeof window.AsyncEndpoint);
    </script>

Ecmascript Modules

Ecmascript modules are available in two flavors of ecmascript modules:

MJS

In a node application, the "import" keyword can be used to import the package.

    //index.mjs
    import * as AsyncEndpoint from "async-endpoint/mjs";

As of this writing, node requires the the "experimental-modules" and "harmony_async_iteration" flags to be set, but this will change once a the "import" and "asynchronous iterator" features hae landed.

node --experimental-modules --harmony_async_iteration index.mjs

If you wish to avoid experimental the features, use the above common.js module.

JS

In a browser application, the "import" keyword can be used to import the package.

When using ".mjs" files, a server may fail to serve the proper "application/javascript" mime type causing the application to fail. As such, the "js" folder is included.

    <script>
        import * as AsyncEndpoint from "async-endpoint/js/index.js";
        <script>
        alert(typeof window.AsyncEndpoint);
        </script>
    </script>

As of this writing, only Chrome supports the necessary import and asynchronous interation features necessary to get this to work.

To ensure compatibility with other browsers use the above browser.js module. You can also re-bundle either the flow, js, or mjs folders.

//Use
import map from "async-endpoint/js/array-like/map.js";
//rather than 
import {map} from "async-endpoint/js/index.js";

API

Classes

AsyncArrayArray

Functions

composePrograms(request, ...programs)AsynchornousIterator

composes programs sequentially with a single input

creates an iterator whose values are mapped from another(iterator, mapper)AsynchornousIterator
executes a provided funcition for each item of an iterator(iterator, handler)undefined
filter(iterator, filterer)AsynchornousIterator

creates an iterator whose values are filtered from another

reduce(iterator, reducer, [inital], [condition], [resetInitial])AsynchornousIterator

creates an iterator whose values are reduced from another

reduceRight(iterator, reducer, [inital], [condition], [resetInitial])AsynchornousIterator

creates an iterator whose values are reduced from another

pause(milliseconds, value)Promise

returns a resolved promise after a given amount of time useful for pausing asynchronous programs

composeAsyncTransformer(last, first)AsyncTransformer

composes two asynchoronous transformers

createQueue(...initial)PushPair

create a queue iterator

createStack(...initial)PushPair

create a stack iterator

createProgramQueue()PushPair

identity program that outputs what ever is input Like "queue", but accepts program as input

createProgramStack()PushPair

identity program that outputs what ever is input Like "queue", but accepts program as input

take(iterator, num, [skip])Promise.<Array>

extract items from iterator as array

takeWhile(iterator, accept, [skip])Promise.<Array>

extract first set of items that match a given condition as an array

identity([delay], request)AsynchronousIterator

program that outputs what ever is put throught

continuousOutput([sample])AsynchronousIterator

program that takes no input and contiuously outputs result of calling function

renderer([...targets])AsyncRenderFunction

creates a render function that renders yeilded results from programs to any number of target functions. If no targets are given, objects will be rendered using "console.log" Can be used as a "passthrough" (see "createQueue" example)

tee(...programs)AsyncRenderFunction

creates a render function whos's values are teed on to given It may be advantageous to use this along side a programQueue

inputConsole(respond)

send input typed into console to a PairedRespond function

inputPipe(respond)

send input piped to console to a PairedRespond function

Typedefs

PairedRequestPromise.<*>

a function that receives it's response from a paired PairedRespond function

PairedRespond : function

a function that sends it's input to a paired PairedRequest function

AsyncTransformer*

stateless asynchronous function that transforms input without side effects

ProgramAsynchronousIterator

an iteractive program

AsyncRenderFunction : function

a function that renders values from a given [Asynchronous] Iterator

PushPair : Array

an iterator and a paired function to add to it

AsyncArray ⇐ Array

Kind: global class
Extends: Array

new AsyncArray()

An Asynchronous Array

Example

import {AsyncArray} from "async-endpoint";
const input = new AsyncArray();
const main = async()=>{
     setTimeout(()=>{
         input.push("hello world");
     })
     const {value} = await input.next();
     console.log(value);
}
main();
//logs "hello world"

Example

import {AsyncArray} from "async-endpoint";
const input = new AsyncArray();
const main = async()=>{
     setTimeout(()=>{
         input.push("hello");
         input.push("world");
     })
     for await(const value of input){
       console.log(vaue);
     }
}
main();
//logs "hello"
//logs "world"

composePrograms(request, ...programs) ⇒ AsynchornousIterator

composes programs sequentially with a single input

Kind: global function
Returns: AsynchornousIterator - resulting iterator

Param Type Description
request function request function for input
...programs Program programs to be composed sequentially

Example

import {composePrograms, AsyncArray} from "async-endpoint";
import porgram1, program1, program3 from "....js";
const channel = new AsyncArray();
const respond = channel.push.bind(channel),
request = async () => (await channel.next()).value;
const program = composePrograms(request, program1, program2, program3);
window.respond = respond;

creates an iterator whose values are mapped from another(iterator, mapper) ⇒ AsynchornousIterator

Kind: global function
Returns: AsynchornousIterator - resulting iterator

Param Type Description
iterator AsynchornousIterator iterator to be mapped
mapper AsyncTransformer transformation for individual items

Example

import {map, continuousOutput},  from "async-endpoint";
let i = 0;
const mapped = map(continuousOutput(()=>i++), (n) => n + 2);
const main = async ()=>{
 for await(item of mapped){
     console.log(item);
 }
}
main();
logs "2"
logs "3"
logs "4"
...

executes a provided funcition for each item of an iterator(iterator, handler) ⇒ undefined

Kind: global function

Param Type Description
iterator AsynchornousIterator iterator
handler AsyncTransformer provided function

Example

import {forEach, continuousOutput},  from "async-endpoint";
let i = 0;
forEach(continuousOutput(()=>i++, console.log));
main();
logs "2"
logs "3"
logs "4"
...

filter(iterator, filterer) ⇒ AsynchornousIterator

creates an iterator whose values are filtered from another

Kind: global function
Returns: AsynchornousIterator - filtered iterator

Param Type Description
iterator AsynchornousIterator iterator to be filtered
filterer function boolean filtering function

Example

import {filter, continuousOutput} from "async-endpoint";
let i = 0;
const filtered =filter(continuousOutput(()=>i++),  (n)=>n%2);
const main = async ()=>{
 for await(item of filtered){
     console.log(item);
 }
}
main();
logs "1"
logs "3"
logs "5"

reduce(iterator, reducer, [inital], [condition], [resetInitial]) ⇒ AsynchornousIterator

creates an iterator whose values are reduced from another

Kind: global function
Returns: AsynchornousIterator - reduced iterator

Param Type Default Description
iterator AsynchornousIterator iterator to be reduced
reducer function reducing function
[inital] * initial object to reduce into
[condition] function (item, initial) => false boolean filtering function indicating when to start new reduction phase
[resetInitial] function ()=>initial method to reset/replace initial reduction object

Example

import {reduce, continuousOutput} from "async-endpoint";
let i = 0;
const reduced = reduce(continuousOutput(()=>i++) , (previous, current)=>previous.push(current),[], (x)=!(x%5), ()=>([]));
const main = async ()=>{
 for await(item of reduced){
     console.log(item);
 }
}
main();
logs "[0]"
logs "[1, 2, 3, 4, 5]"
 ...

reduceRight(iterator, reducer, [inital], [condition], [resetInitial]) ⇒ AsynchornousIterator

creates an iterator whose values are reduced from another

Kind: global function
Returns: AsynchornousIterator - reduced iterator

Param Type Default Description
iterator AsynchornousIterator iterator to be reduced
reducer function reducing function
[inital] * initial object to reduce into
[condition] function (item, initial) => false boolean filtering function indicating when to start new reduction phase
[resetInitial] function ()=>initial method to reset/replace initial reduction object

Example

import {reduce, continuousOutput} from "async-endpoint";
let i = 0;
const reduced = reduce(continuousOutput(()=>i++) , (previous, current)=>previous.push(current),[], (x)=!(x%5), ()=>([]));
const main = async ()=>{
 for await(item of reduced){
     console.log(item);
 }
}
main();
logs "[0]"
logs "[1, 2, 3, 4, 5]"
 ...

pause(milliseconds, value) ⇒ Promise

returns a resolved promise after a given amount of time useful for pausing asynchronous programs

Kind: global function
Returns: Promise - promise fulfilled with value

Param Type Description
milliseconds Number time to pause
value * optional returned value

Example

import {pause} from "async-endopint.js";
const main = async ()=>{
 console.log("hello");
 await pause(1000);
 console.log("goodbye");
}
main();
//logs "hello"
//logs "goodbye" (after 1 second)

composeAsyncTransformer(last, first) ⇒ AsyncTransformer

composes two asynchoronous transformers

Kind: global function
Returns: AsyncTransformer - asynchonous compositon of current and pre

Param Type Description
last AsyncTransformer transformer to apply last
first AsyncTransformer transformer to apply first

Example

import {composeAsyncTransformer} from "async-endopint.js";
const t1 = async (x)=>`<${x}>`;
const t2 = async (x)=>`[${x}]`;
const t = composeAsyncTransformer(t1, t2);
t("hello").then(console.log);
//logs "<[hello]>"

createQueue(...initial) ⇒ PushPair

create a queue iterator

Kind: global function
Returns: PushPair - queue and push function

Param Type Description
...initial * initial items in queue

Example

import {createQueue, renderer, renderer as createPassThrough} from "async-endpoint";
import porgram1, program1, program3 from "....js";
const [queue, push] = createQueue();
const passthrough = createPassThrough(push);
passthrough(porgram1(), program2(), program3());
const render = renderer();
render(queue);

createStack(...initial) ⇒ PushPair

create a stack iterator

Kind: global function
Returns: PushPair - stack and push function

Param Type Description
...initial * initial items on stack

Example

import {createStack, renderer, renderer as createPassThrough} from "async-endpoint";
import porgram1, program1, program3 from "....js";
const [stack, push] = createStack();
const passthrough = createPassThrough(push);
passthrough(porgram1(), program2(), program3());
const render = renderer();
render(stack);

createProgramQueue() ⇒ PushPair

identity program that outputs what ever is input Like "queue", but accepts program as input

Kind: global function
Returns: PushPair - iterator and push function
Example

import {createProgramQueue, renderer} from "async-endpoint";
import porgram1, program1, program3 from "....js";
const [queue, push] = createProgramQueue();
push(porgram1(), program2(), program3());
const render = renderer();
render(queue);

createProgramStack() ⇒ PushPair

identity program that outputs what ever is input Like "queue", but accepts program as input

Kind: global function
Returns: PushPair - iterator and push function
Example

import {createProgramStack, renderer} from "async-endpoint";
import porgram1, program1, program3 from "....js";
const [stack, push] = createProgramStack();
push(porgram1(), program2(), program3());
const render = renderer();
render(stack);

take(iterator, num, [skip]) ⇒ Promise.<Array>

extract items from iterator as array

Kind: global function
Returns: Promise.<Array> - stack and push function

Param Type Default Description
iterator AsynchronousIterator iterator from which to take
num Number number of items to take from iterator
[skip] Number 0 number of items to skip before taking

Example

import {take, continuousOutput} from "async-endpoint";
let i = 0;
take(continuousOutput(()=>i++), 3,1).then(taken=>console.log(taken));
//logs "[1,2,3]"

takeWhile(iterator, accept, [skip]) ⇒ Promise.<Array>

extract first set of items that match a given condition as an array

Kind: global function
Returns: Promise.<Array> - stack and push function

Param Type Default Description
iterator AsynchronousIterator iterator from which to take
accept function boolean filtering function indicating whether to allow item
[skip] Number 0 number of items to skip before taking

Example

import {takeWhile, continuousOutput} from "async-endpoint";
let i = 0;
takeWhile(continuousOutput(()=>i++), x  => x < 5, 2).then(taken=>console.log(taken));
//logs "[2,3,4]"

identity([delay], request) ⇒ AsynchronousIterator

program that outputs what ever is put throught

Kind: global function
Returns: AsynchronousIterator - resulting iterator

Param Type Default Description
[delay] * 0 delay between sending output
request PairedRequest request function for input

Example

import {identity, renderer, AsyncArray} from "async-endpoint";
const channel = new AsyncArray();
const respond = channel.push.bind(channel),
request = async () => (await channel.next()).value;
identity(undefined, request);
window.respond = respond

continuousOutput([sample]) ⇒ AsynchronousIterator

program that takes no input and contiuously outputs result of calling function

Kind: global function
Returns: AsynchronousIterator - resulting iterator

Param Type Default Description
[sample] * ()=>{} function whose result to output

Example

import {continuousOutput, renderer} from "async-endpoint";
const render = renderer();
render(continuousOutput(()=>"hello"))
logs "hello" (continously)
...

renderer([...targets]) ⇒ AsyncRenderFunction

creates a render function that renders yeilded results from programs to any number of target functions. If no targets are given, objects will be rendered using "console.log" Can be used as a "passthrough" (see "createQueue" example)

Kind: global function
Returns: AsyncRenderFunction - asychronous render function

Param Type Description
[...targets] function request function for input

Example

import {renderer, continuousOutput} from "async-input.js";
const render = renderer();
render(continuousOutput);
//logs "0"
//logs "1"
...

tee(...programs) ⇒ AsyncRenderFunction

creates a render function whos's values are teed on to given It may be advantageous to use this along side a programQueue

Kind: global function
Returns: AsyncRenderFunction - asychronous render function

Param Type Description
...programs Program programs to be sent values

Example

import {tee, continousOutput, renderer} from "async-endpoint";
import porgram1, program1, program3 from "....js";
const instance1 = program1();
const instance2 = program2();
const instance3 = program3();
const render = renderer();
render(instance1, instance2, instance3)
const renderTee = tee(porgram1, program1, program3)
renderTee(continousOutput())

inputConsole(respond)

send input typed into console to a PairedRespond function

Kind: global function

Param Type Description
respond PairedRespond request function for input

Example

import {identity, AsyncArray, renderer} from "async-endpoint";
import inputConsole from "async-endpoint/input/console";
const channel = new AsyncArray();
const respond = channel.push.bind(channel),
request = async () => (await channel.next()).value;
const render = renderer();
render(identity(undefined, request))
inputConsole(respond);

inputPipe(respond)

send input piped to console to a PairedRespond function

Kind: global function

Param Type Description
respond PairedRespond request function for input

Example

import {identity, renderer, AsyncArray} from "async-endpoint";
import inputPipe from "async-endpoint/input/pipe";
const channel = new AsyncArray();
const respond = channel.push.bind(channel),
request = async () => (await channel.next()).value;
const render = renderer();
render(identity(undefined, request))
inputPipe(respond);

PairedRequest ⇒ Promise.<*>

a function that receives it's response from a paired PairedRespond function

Kind: global typedef
Returns: Promise.<*> - response from respond reunction

PairedRespond : function

a function that sends it's input to a paired PairedRequest function

Kind: global typedef

Param Type Description
response * response for request function

AsyncTransformer ⇒ *

stateless asynchronous function that transforms input without side effects

Kind: global typedef
Returns: * - transformed input

Param Type Description
input * input

Program ⇒ AsynchronousIterator

an iteractive program

Kind: global typedef
Returns: AsynchronousIterator - asynchronous iterator result

Param Type Description
init *
request PairedRequest request function for input

AsyncRenderFunction : function

a function that renders values from a given [Asynchronous] Iterator

Kind: global typedef

Param Type
program_return; AsynchronousIterator

PushPair : Array

Deprecated

an iterator and a paired function to add to it

Kind: global typedef
Properties

Name Type Description
0 AsynchornousIterator iterator
1 function function used to add to iterator

Readme

Keywords

none

Package Sidebar

Install

npm i async-endpoint

Weekly Downloads

32

Version

0.0.23

License

MIT

Unpacked Size

416 kB

Total Files

159

Last publish

Collaborators

  • johnhenry