0.0.23 • Published 6 years ago

async-endpoint v0.0.23

Weekly downloads
2
License
MIT
Repository
github
Last release
6 years ago

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

    • Synchronous Endpoints
    • Synchronous Iteration
    • Asynchronous Iteration
    • Asynchronous Input
    • Import
    • API

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

Functions

Typedefs

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

ParamTypeDescription
requestfunctionrequest function for input
...programsProgramprograms 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

ParamTypeDescription
iteratorAsynchornousIteratoriterator to be mapped
mapperAsyncTransformertransformation 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

ParamTypeDescription
iteratorAsynchornousIteratoriterator
handlerAsyncTransformerprovided 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

ParamTypeDescription
iteratorAsynchornousIteratoriterator to be filtered
filtererfunctionboolean 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

ParamTypeDefaultDescription
iteratorAsynchornousIteratoriterator to be reduced
reducerfunctionreducing function
inital*initial object to reduce into
conditionfunction(item, initial) => falseboolean filtering function indicating when to start new reduction phase
resetInitialfunction()=>initialmethod 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

ParamTypeDefaultDescription
iteratorAsynchornousIteratoriterator to be reduced
reducerfunctionreducing function
inital*initial object to reduce into
conditionfunction(item, initial) => falseboolean filtering function indicating when to start new reduction phase
resetInitialfunction()=>initialmethod 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

ParamTypeDescription
millisecondsNumbertime 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

ParamTypeDescription
lastAsyncTransformertransformer to apply last
firstAsyncTransformertransformer 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

ParamTypeDescription
...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

ParamTypeDescription
...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

ParamTypeDefaultDescription
iteratorAsynchronousIteratoriterator from which to take
numNumbernumber of items to take from iterator
skipNumber0number 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

ParamTypeDefaultDescription
iteratorAsynchronousIteratoriterator from which to take
acceptfunctionboolean filtering function indicating whether to allow item
skipNumber0number 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

ParamTypeDefaultDescription
delay*0delay between sending output
requestPairedRequestrequest 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

ParamTypeDefaultDescription
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

ParamTypeDescription
...targetsfunctionrequest 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

ParamTypeDescription
...programsProgramprograms 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

ParamTypeDescription
respondPairedRespondrequest 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

ParamTypeDescription
respondPairedRespondrequest 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

ParamTypeDescription
response*response for request function

AsyncTransformer ⇒ *

stateless asynchronous function that transforms input without side effects

Kind: global typedef
Returns: * - transformed input

ParamTypeDescription
input*input

Program ⇒ AsynchronousIterator

an iteractive program

Kind: global typedef
Returns: AsynchronousIterator - asynchronous iterator result

ParamTypeDescription
init*
requestPairedRequestrequest function for input

AsyncRenderFunction : function

a function that renders values from a given Asynchronous Iterator

Kind: global typedef

ParamType
program_return;AsynchronousIterator

PushPair : Array

Deprecated

an iterator and a paired function to add to it

Kind: global typedef
Properties

NameTypeDescription
0AsynchornousIteratoriterator
1functionfunction used to add to iterator