SkillAgentSearch skills...

Masync

Statically-typed Monadic Asynchronous Control Flow Library for JavaScript/TypeScript/AltJSs

Install / Use

/learn @kontan/Masync
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

masync

Statically-typed Monadic Asynchronous Control Flow Library for JavaScript/TypeScript/AltJSs

Abstract

masync is an asynchronous control flow library for JavaScript, TypeScript and other AltJSs. masync shares the goal with jQuery.deferred or async.js but masync is not based on any Promises or Deferreds. masync is simpler than Promise but more flexible and powerful. You are no longer bothered by Callback Hell because you can write asynchronous codes like a syncronous codes with masync.

masync is standalone so it doesn't need other libraries such as jQuery or async.js. Furthermore you can exchange Promises, Node-style callback functions and Generators with asynchronous data of masync.

masync doesn't need any special features of JavaScript such as generators, Web Workers or fibers. You can use masync everywhare JavaScript runs.

Statically-typed APIs are an exclusive property of masync. Whole of masync is written in TypeScript and you can statically verify type consistency of your TypeScript source code, however you can also use it from JavaScript or other AltJSs.

A framework of masync is based on Monad and masync is inspired by IO Monad in Haskell. A synchronous object of masync is Monad. This framework is very simple, though, thus flexible and powerful.

Examples

Let's see a first example. Create a HTML file that has following codes.

<script type="text/javascript" src="https://rawgithub.com/kontan/masync/master/masync.js"></script>
<script type="text/javascript">
masync.run(
    masync.log("Hello, "),
    masync.wait(3),
    masync.log("asynchronous world!")
);
</script>

Create a new tab and open JavaScript developer's console. Then drag and drop the HTML file to the empty tab to execute the code. At first you will see "Hello, " in your console, then you will get "asynchronous world!" three seconds later. masync.wait stops execution in the specified milliseconds. masync.log prints a text like console.log in console. masync.run begin those tasks sequentially. However, don't mistake asynchronous for synchronous. These codes run in asynchronous.

Here's next example. masync.get send XMLHttpRequest and return the data in asynchronous. You can pass the result data to masync.log directly. You don't need to use your callback function to finish the task. The following code requests first.txt and prints contents of the text file, then requests second.txt and print it in console. It's looks like synchronous, but asynchronous.

masync.run(
    masync.log(masync.get("first.txt")),
    masync.log(masync.get("second.txt"))        
);

If you want to process those tasks in parallel, you can use parallel function as below:

masync.run(
    masync.parallel(
        masync.log(masync.get("first.txt")),
        masync.log(masync.get("second.txt"))
    )
);

Both of requests begin in parallel. If first.txt is much larger than seconds.txt, you will see the content of second.txt in advance of first.txt in the console. You can combine parallel and sequential tasks at will.

sequential and parallel process

You can freely combinate sequential tasks and parallel task. For example, let a, b ... h are synchronous task,

masync.run(
    a,
    masync.parallel(b, masync.series(c, d)),
    masync.parallel(e, masync.series(f, g)),
    h
);

In a diagram:

  --------------------------------------------> time 
a ===>
b     =============>
c     ====>
d          ====>
e                   ========>
f                   =====>
g                         ======>
h                                ====>

statelessness

Most of asynchronous objects are stateless. It means you can reuse a asynchronous object in a variety of positions all you want. This code prints "Hey!" thrice in console. You are not a cause for concern about how many event handlers the asynchronous object has.

var hey = masync.log("Hey!");
masync.run(
    hey,
    masync.wait(1),
    hey,
    masync.wait(0.5),
    hey
);

Function Application to Asynchronous Objects

You can apply a asynchronous function to asynchronous data. The following code prints "HELLO!".

var hello = masync.pure("Hello!");
masync.run(masync.log(masync.toUpperCase(hello)));

Raw Data Access

If you want to access a raw result of an asynchronous task in the worst way, cache and inject is useful. You can get a raw value from a cached asynchronous object by () after evaluation in callback function of inject.

var a = masync.cache(masync.get("a.txt"));
masunc.run(
    a,  // evaluate a
    masync.inject(function(){
        console.log(a().toUpperCase()); // a() is a raw string value
    });
);

Events Handling Without Callback

Also in event handling, callbacks are not needed. All you got to do is wait for an event to happen. For example, if you want to print a message "Button pressed!" when a button is pressed:

masync.run(
    masync.waitForMouseDown(button),
    masync.log("Button pressed!")
);

API Reference

Primitive Functions


pure

pure(v: T): Async<T>

Construct a Async object from a regular value. For example, pure(42) is Async object that do nothing but returns just 42 in asynchronous.


lift

lift(f: (a: A, ..., y: Y) => Z): (a: Async<A>, ..., y: Async<Y>) => Async<Z>

Lift a regular function up to a Async function. CAUTION: lift takes no thought for this. So lift(console.log) is invalid because console.log needs console as this when it's be called. You need to write as lift(console.log.bind(console)).


fmap

fmap(f: (a: A) => B, a: Async<A>) => Async<B>

Apply a regular function f to a Async object a. If the parameter function have 2 or more parameters, you should use lift instead of fmap.

function toUpperCase(str){
    return str.toUpperCase();
}

masync.run(
    masync.log(masync.fmap(toUpperCase, masync.get("hoge.txt")))
);

This function is also useful when you want to access the raw result value of an asynchronous task.

masync.run(
    masync.fmap(function(hoge){
        console.log(hoge);
    }, masync.get("hoge.txt"));
);

ap

ap(f: Async<(a: A) => B>, a: Async<A>) => Async<B>

Apply a Async function to a Async object. You will not use it.


bind

bind(x: Async<A>, f: (a: A)=>Async<B>) => Async<B>

Bind a Async object and a function. The function f receives a value from x. Example:

masync.run(
    masync.bind(masync.get("hoge"), masync.log)
)

Control Flow


series

series(a: Async<A>, b: Async<B>, ...., z: Async<Z>): Async<Z>

Do those tasks sequentially in asynchronous. Example:

maysync.run(
    masync.parallel(
        masync.series(
            masync.wait(1), 
            masync.log("Hello, ")
        ),
        masync.series(
            masync.wait(2), 
            masync.log("World!")
        )
    )
);

This prints "Hello, " a seconds later and "World!" prints two seconds later.

  series(a, b, c)

  |Start                            |Finish
  |------------------------------------------------> time
a |==============>                  |
b |               =======>          |
c |                       =========>|

parallel

parallel(a: Async<A>, b: Async<B>, ...., z: Async<Z>): Async<void>

Do tasks in parallel. Return values are discard. If you need to the return value, you can cache function.

var hoge = masync.cache(masync.get("hoge.txt"));
var piyo = masync.cache(masync.get("piyo.txt"));
maysync.run(
    masync.parallel(hoge, piyo),
    masync.log(masync.strcat(hoge, piyo))
);

This code begin both of XHR requests at the same time. In second evaluation of hoge and piyo, it does't request and returns cached value.

  parallel(a, b, c)

  |Start                          |Finish
  |------------------------------------------------> time
a |==============================>|
b |=============>                 |
c |=====================>         |

fastest

fastest(a: Async<T>, b: Async<T>, ...., z: Async<T>): Async<T>

Do tasks in parallel but finish when at least one task finished. It's useful for handling a timeout.

masync.log(
    masync.fastest(
        masync.get("a.txt"),
        masync.seq(
            masync.wait(10),
            masync.pure("Error: timeouted.")
        )
    )
)

In a diagram:

  fastest(a, b, c)

  |Start               |Finish
  |------------------------------------------------> time
a |===========================>
b |===================>|
c |===================================>

branch

branch(primal: Async<T>, sub: Async<any>): Async<T>

Runs sub task repeatedly until main finish.

branch(a, b)

  |Start                            |Finish
  |------------------------------------------------> time
a |================================>|    
b |=======>=====>=======>======>=======>

repeat

repeat(a: Async<any>, ..., z: Async<any>): Async<void>

Repeats tasks forever. This task never finish.

repeat(a, b, c)

  |Start                      
  |----------------------------------------
View on GitHub
GitHub Stars45
CategoryDevelopment
Updated4y ago
Forks3

Languages

TypeScript

Security Score

60/100

Audited on Oct 7, 2021

No findings