npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@task.flow/task

v0.1.0

Published

Library for describing async operations as data & managing them as explicit effects

Downloads

6

Readme

outtask

travis npm downloads js-standard-style

Library allows describing asynchronous operations that may fail, like HTTP requests or writing to a database and provides tools to manage them. Library also provides a light weight process / thread abstraction so you can get bunch of different tasks running concurrently.

Primary goal of this library is to provide an alternative to programming with side-effects in JS, by programming with managed effects. Task abstraction just describes operation, performing it is a job of scheduler, which provides a handy separation. Think of tasks as an items on the todo list - they just describe things to do, who will do those tasks and when is a separate concern, concern of scheduler.

Usage

All examples presume following import:

import Task from 'outtask'

Task <error, value>

In nutshell Task <error, value> type represents a task that may fail with a specific error or succeed with a specific value.

For example, maybe we have a task with the type Task<string, User>. This implies that when task is performed, it may either fail with a string message describing error or succeed with a value of User (instance). For example this task could be asking a server for a certain user.

Note: Thinking about task types is useful as that gives very specific insight what the result of performing that task will look like. Library is written in flow to take advantage of type checker and to let you catch all the possible errors associated with invalid uses of it early on.

succeed

A task that succeeds immediately when run with a value provided:

Task
  .succeed(42)
  .fork(console.log, econsole.error)

// => Log: 42

fail

A task that fails immediately when run with a value provided:

Task
  .fail("file not found")
  .fork(console.log, console.error)

// => Error: "file not found"

Mapping

map

Task
  .succeed(4)
  .map(x => x * 2)
  .fork(console.log, console.error)
// => Log: 8

map2

Task.map2((a, b) => a + b, Task.succeed(2), Task.succeed(3))
  .fork(console.log, console.error)
// => Log: 5

map3

Task.map3((a, b, c) => a + b + c,
          Task.succeed(2),
          Task.succeed(3),
          Task.succeed(4))
  .fork(console.log, console.error)
// => Log: 9

map4

Task.map4((a, b, c, d) => a * b - c + d],
          Task.succeed(2),
          Task.succeed(3),
          Task.succeed(4),
          Task.succeed(5))
  .fork(console.log, console.error)
// => Log: 7

map5

Task.map5((a, b, c, d, e) => a * b + c + d / e],
          Task.succeed(2),
          Task.succeed(3),
          Task.succeed(4),
          Task.succeed(5),
          Task.succeed(2))
  .fork(console.log, console.error)
// => Log: 12.5

chaining

chain

Chain together a task and a callback. The first task will run, and if it is successful, you give the another task from the callback which will be performed next.

Task
  .succeed(2)
  .chain(n => Task.succeed(n + 2)) // Succeed(4)
  .fork(console.log, console.error)

// => Log: 4

Task
  .fail('Boom')
  .chain(n => Task.succeed(n + 2)) // Succeed(4)
  .fork(console.log, console.error)

// => Error: 'Boom'

sequence

Start with a list of tasks, and turn them into a single task that returns an array. The tasks will be run in order one-by-one and if any task fails the whole sequence fails.

Task
  .sequence(fetch(url1), fetch(url2))
  .fork(console.log, console.error)


// => Log: [contentURL1, contentURL2]

Errors

capture

Recover from a failure in a task. If the given task fails, we use the callback to turn error into another fallback task.

Task
  .fail('Boom')
  .capture(error => Task.succeed(5))
  .fork(console.log, console.error)
// => Log: 5

Task
  .fail('Boom')
  .capture(error => Task.fail('Oops'))
  .fork(console.log, console.error)

// => Error: Oops

format

Transform the error value. This can be useful if you need a bunch of error types to match up.

Task
  .fail('Boom')
  .format(Error)
  .fork(console.log, console.error)

// => Error: Error('Boom')

Task
  .fail({ code: 15 })
  .format(JSON.stringify)
  .format(Error)
  .fork(console.log, console.error)

// => Error: Error('{code:15}')

recover

Transform the error value into success value. Useful if you need never failing tasks:

Task
  .fail('Boom')
  .map(value => { ok: value })
  .recover(error => { error: error })
  .fork(console.log, console.error)

// => Log: { error: 'Boom' }

Task
  .succeed('Data')
  .map(value => { ok: value })
  .recover(error => { error: error })
  .fork(console.log, console.error)

// => Log: { ok: 'Data' }

Bulit-in tasks

sleep

Task that wait for given number of milliseconds and then succeeds with void:

Task
  .sleep(50)
  .chain(_ => Task.succeed(5))
  .fork(console.log, console.error)

// => Log: 5 // prints in 50ms

requestAnimationFrame

Task succeeds with DOMHighResTimeStamp on next animation (It's just a requestAnimationFrame wrapped in task API):

Task
  .requestAnimationFrame()
  .fork(console.log, console.error)

// => Log: 124256.00000000001

Custom tasks

You can wrap arbitrary code into a task. API is intentionally similar to Promise API:

const store =
  (key:string, value:string):Task<Error, void> =>
  new Task((succeed, fail) => {
    try {
      window.localStorage[key] = value
      succeed()
    } catch (error) {
      fail(error)
    }
  })

Custom cancellable tasks

You can write cancellable task, API is pretty similar to custom tasks only difference is you need to provide a second abort function which will be passed whatever you return from first function in case task is aborted (or process is killed):

const fetch =
  (url:string):Task<Error, String> =>
  new Task((succeed, fail):XMLHttpRequest => {
    const request = new XMLHttpRequest()
    request.open('GET', url, true)
    request.responseType = 'text'
    request.onerror = event => {
      fail(Error(`Network request to ${url} has failed: ${request.statusText}`))
    }
    request.ontimeout = event => {
      fail(Error(`Network request to ${url} timed out`))
    }
    request.onload = event => {
      succeed(request.responseText)
    }

    request.send()
    return request
  }, (request:XMLHttpRequest):void => {
    request.abort()
  })

Process <exit, message>

Process <error, value> represents an execution of Task <error, value>. Many such processes can be spawn to execute different tasks concurrently. All the undocumented fields and methods of the Process class are considered internal implementation details that may change at any point without warning so do not depend on them in any way.

fork <x, a> (onSucceed:(v:a)=>void, onFail:(e:x)=>void):Process<x, a>

To run a task you need to provide success and failure handlers to a .fork which will start a light-weight process.

const process =
  Task
  .sleep(50)
  .chain(_ => Task.succeed(5))
  .fork(console.log, console.error)

Note: Most of the time you can just ignore process when forking a task, although not always following sections will illustrate those cases.

kill: <x, y, a> (process:Process<x, a>) => Task<y, void>

Sometimes you spawn a task, but later due to some circumstances you decide to abort it. In such cases you can kill process to abort whatever task it was busy with. As in an example below HTTP request will be aborted while in flight.

const process =
  fetch('http://elm-lang.org')
  .map(JSON.parse)
  .fork(console.log, console.error)

Task
  .kill(process)
  .fork(console.log, console.error)

spawn <x, y, a> (task:Task<x, a>) => Task<y, Process<x, a>>

Run a task in its own light-weight process. In the following example, task1 and task2 will be concurrent. If task1 makes an HTTP request, then task2 will do some work while request is pending.

task1
  .spawn()
  .chain(process => task)

Install

npm install outtask

Prior art