Giter VIP home page Giter VIP logo

creed's Introduction

creed :: async

Greenkeeper badge

Join the chat at https://gitter.im/briancavalier/creed

Sophisticated and functionally-minded async with advanced features: coroutines, promises, ES2015 iterables, fantasy-land.

Creed simplifies async by letting you write coroutines using ES2015 generators and promises, and encourages functional programming via fantasy-land. It also makes uncaught errors obvious by default, and supports other ES2015 features such as iterables.

You can also use babel and the babel-creed-async plugin to write ES7 async functions backed by creed coroutines.

Promises/A+ Fantasy Land Build Status Coverage Status

Example

Using creed coroutines, ES2015, and FP to solve the async-problem:

import { runNode, all, coroutine } from 'creed'
import { readFile } from 'fs'
import { join } from 'path'

// joinPath :: String -> String -> String
const joinPath = init => tail => join(init, tail)

// readFileP :: String -> String -> Promise Error Buffer
const readFileP = encoding => file => runNode(readFile, file, {encoding})

// pipe :: (a -> b) -> (b -> c) -> (a -> c)
const pipe = (f, g) => x => g(f(x))

// concatFiles :: String -> Promise Error String
const concatFiles = coroutine(function* (dir) {
    const readUtf8P = pipe(joinPath(dir), readFileP('utf8'))

    const index = yield readUtf8P('index.txt')
    const results = yield all(index.match(/^.*(?=\n)/gm).map(readUtf8P))
    return results.join('')
})

const main = process => concatFiles(process.argv[2])
    .then(s => process.stdout.write(s))

main(process)

Get it

npm install --save creed

bower install --save creed

As a module:

// ES2015
import { resolve, reject, all, ... } from 'creed';

// Node/CommonJS
var creed = require('creed')

// AMD
define(['creed'], function(creed) { ... })

As window.creed:

<!-- Browser global: window.creed -->
<script src="creed/dist/creed.js"></script>

Try it

Creed will work anywhere ES5 works. Here's how to try it.

Creed is REPL friendly, with instant and obvious feedback. Try it out in JSBin or using ES2015 with babel, or try it in Node >= 6:

npm install creed
node
> let { resolve, delay, all, race } = require('creed')
undefined
> resolve('hello')
Promise { fulfilled: hello }
> all([1, 2, 3].map(resolve))
Promise { fulfilled: 1,2,3 }
> let p = delay(1000, 'done!'); p
Promise { pending }

... wait 1 second ...

> p
Promise { fulfilled: done! }
> race([delay(100, 'no'), 'winner'])
Promise { fulfilled: winner }

Errors & debugging

By design, uncaught creed promise errors are fatal. They will crash your program, forcing you to fix or .catch them. You can override this behavior by registering your own error event listener.

Consider this small program, which contains a ReferenceError.

import { all, runNode } from 'creed';
import { readFile } from 'fs';

const readFileP = file => runNode(readFile, file)

const readFilesP = files => all(files.map(readFileP))

const append = (head, tail) => head + fail; // Oops, typo will throw ReferenceError

// Calling append() from nested promise causes
// a ReferenceError, but it is not being caught
readFilesP(process.argv.slice(2))
    .map(contents => contents.reduce(append, ''))
    .then(s => console.log(s))

Running this program (e.g. using babel-node) causes a fatal error, exiting the process with a stack trace:

> babel-node experiments/errors.js file1 file2 ...
/Users/brian/Projects/creed/dist/creed.js:672
		throw value;
		^

ReferenceError: fail is not defined
    at append (/Users/brian/Projects/creed/experiments/errors.js:8:39)
    at Array.reduce (native)
    at readFilesP.map.contents (/Users/brian/Projects/creed/experiments/errors.js:13:31)
    at tryCall (/Users/brian/Projects/creed/dist/creed.js:344:12)
    at Map.fulfilled (/Users/brian/Projects/creed/dist/creed.js:408:3)
    at Fulfilled._runAction (/Users/brian/Projects/creed/dist/creed.js:945:10)
    at Future.run (/Users/brian/Projects/creed/dist/creed.js:871:5)
    at TaskQueue._drain (/Users/brian/Projects/creed/dist/creed.js:131:8)
    at /Users/brian/Projects/creed/dist/creed.js:117:53
    at _combinedTickCallback (internal/process/next_tick.js:67:7)

Async traces

Experimental: V8 only

Fatal stack traces are helpful, but sometimes they aren't enough. Enable async traces for stack traces for even more detail.

Note: Enabling async traces is typically an application-level concern. Libraries that use creed should not enable them in dist builds.

Running the example above with async traces enabled yields a more helpful trace. Notably:

  • asynchronous stack frames are shown: both the point at which map is called and the point in the mapping function (which is called asynchronous) are shown.
  • the Map operation is called out specifically
  • stack frames from within creed are omitted
> CREED_DEBUG=1 babel-node experiments/errors.js file1 file2 ...
/Users/brian/Projects/creed/dist/creed.js:672
		throw value;
		^

ReferenceError: fail is not defined
    at append (/Users/brian/Projects/creed/experiments/errors.js:8:39)
    at Array.reduce (native)
    at readFilesP.map.contents (/Users/brian/Projects/creed/experiments/errors.js:13:31)
 from Map:
    at Object.<anonymous> (/Users/brian/Projects/creed/experiments/errors.js:13:6)
    at loader (/Users/brian/Projects/creed/node_modules/babel-register/lib/node.js:144:5)
    at Object.require.extensions.(anonymous function) [as .js] (/Users/brian/Projects/creed/node_modules/babel-register/lib/node.js:154:7)

Enabling async traces

Enable async traces by:

  • NODE_ENV=development or NODE_ENV=test - async traces will be enabled automatically.
  • CREED_DEBUG=1 (or any non-empty string) - enables async traces even if NODE_ENV=production or NODE_ENV not set.
  • enableAsyncTraces() - programatically enable async traces, e.g. for browsers, etc. where env vars aren't available.

Performance impact

Async traces typically have about a 3-4x impact on performance.

That may be just fine for some applications, while not for others. Be sure to assess your application performance needs and budget before running with async traces enabled in production.

Debug events

Creed supports global window events in browsers, and process events in Node, similar to Node's 'uncaughtException' event. This allows applications to register a handler to receive events from all promise implementations that support these global events.

Errors passed to unhandled rejection event handlers will have async traces if they are enabled.

The events are:

  • 'unhandledRejection': fired when an unhandled rejection is detected
  • 'rejectionHandled': fired when rejection previously reported via an 'unhandledRejection' event becomes handled

Node global process events

The following example shows how to use global process events in Node.js to implement simple debug output. The parameters passed to the process event handlers:

  • reason - the rejection reason, typically an Error instance.
  • promise - the promise that was rejected. This can be used to correlate corresponding unhandledRejection and rejectionHandled events for the same promise.
process.on('unhandledRejection', reportRejection)
process.on('rejectionHandled', reportHandled)

function reportRejection(error, promise) {
	// Implement whatever logic your application requires
	// Log or record error state, etc.
}

function reportHandled(promise) {
	// Implement whatever logic your application requires
	// Log that error has been handled, etc.
}

Browser window events

The following example shows how to use global window events in browsers to implement simple debug output. The event object has the following extra properties:

  • event.detail.reason - the rejection reason (typically an Error instance)
  • event.detail.promise - the promise that was rejected. This can be used to correlate corresponding unhandledRejection and rejectionHandled events for the same promise.
window.addEventListener('unhandledRejection', event => {
	// Calling preventDefault() suppresses default rejection logging
	// in favor of your own.
	event.preventDefault()
	reportRejection(event.detail.reason, event.detail.promise)
}, false)

window.addEventListener('rejectionHandled', event => {
	// Calling preventDefault() suppresses default rejection logging
	// in favor of your own.
	event.preventDefault()
	reportHandled(event.detail.promise)
}, false)

function reportRejection(error, promise) {
	// Implement whatever logic your application requires
	// Log or record error state, etc.
}

function reportHandled(promise) {
	// Implement whatever logic your application requires
	// Log that error has been handled, etc.
}

API

Run async tasks

coroutine :: Generator a → (...* → Promise e a)

Create an async coroutine from a promise-yielding generator.

import { coroutine } from 'creed';

function fetchTextFromUrl(url) {
    // Fetch the text and return a promise for it
    return promise;
}

// Make an async coroutine from a generator
let getUserProfile = coroutine(function* (userId) {
    try {
        let profileUrl = yield getUserProfileUrlFromDB(userId)
        let text = yield fetchTextFromUrl(profileUrl)
        return text;
    } catch(e) {
        return getDefaultText()
    }
})

// Call it
getUserProfile(123)
    .then(profile => console.log(profile))

Note: In current implementations of JavaScript, it's not possible to detect with reasonable certainty if the function passed to coroutine is a generator function or not. Creed can't know until the function is executed, causing creed to throw an exception synchronously at run-time.

fromNode :: NodeApi e a → (...* → Promise e a)

type NodeApi e a = ...* → Nodeback e a → ()
type Nodeback e a = e → a → ()

Turn a Node API into a promise API

import { fromNode } from 'creed';
import { readFile } from 'fs';

// Make a promised version of fs.readFile
let readFileP = fromNode(readFile)

readFileP('theFile.txt', 'utf8')
    .then(contents => console.log(contents))

runNode :: NodeApi e a → ...* → Promise e a

type NodeApi e a = ...* → Nodeback e a → ()
type Nodeback e a = e → a → ()

Run a Node API and return a promise for its result.

import { runNode } from 'creed';
import { readFile } from 'fs';

runNode(readFile, 'theFile.txt', 'utf8')
    .then(contents => console.log(contents))

runPromise :: Producer e a → ...* → Promise e a

type Producer e a = (...* → Resolve e a → Reject e → ())
type Resolve e a = a|Thenable e a → ()
type Reject e = e → ()

Run a function to produce a promised result.

import { runPromise } from 'creed';

/* Run a function, threading in a url parameter */
let p = runPromise((url, resolve, reject) => {
    var xhr = new XMLHttpRequest;
    xhr.addEventListener("error", reject)
    xhr.addEventListener("load", resolve)
    xhr.open("GET", url)
    xhr.send(null)
}, 'http://...') // inject url parameter

p.then(result => console.log(result))

Parameter threading also makes it easy to create reusable tasks that don't rely on closures and scope chain capturing.

import { runPromise } from 'creed';

function xhrGet(url, resolve, reject) => {
    var xhr = new XMLHttpRequest;
    xhr.addEventListener("error", reject)
    xhr.addEventListener("load", resolve)
    xhr.open("GET", url)
    xhr.send(null)
}

runPromise(xhrGet, 'http://...')
    .then(result => console.log(result))

merge :: (...* → b) → ...Promise e a → Promise e b

Merge promises by passing their fulfillment values to a merge function. Returns a promise for the result of the merge function. Effectively liftN for promises.

import { merge, resolve } from 'creed';

merge((x, y) => x + y, resolve(123), resolve(1))
    .then(z => console.log(z)) //=> 124

Make promises

future :: () → { resolve: Resolve e a, promise: Promise e a }

type Resolve e a = a|Thenable e a → ()

Create a { resolve, promise } pair, where resolve is a function that seals the fate of promise.

import { future, reject } from 'creed';

// Fulfill
let { resolve, promise } = future()
resolve(123)
promise.then(x => console.log(x)) //=> 123

// Resolve to another promise
let anotherPromise = ...;
let { resolve, promise } = future()
resolve(anotherPromise) //=> make promise's fate the same as anotherPromise's

// Reject
let { resolve, promise } = future()
resolve(reject(new Error('oops')))
promise.catch(e => console.log(e)) //=> [Error: oops]

resolve :: a|Thenable e a → Promise e a

Coerce a value or Thenable to a promise.

import { resolve } from 'creed';

resolve(123)
    .then(x => console.log(x)) //=> 123

resolve(resolve(123))
    .then(x => console.log(x)) //=> 123

resolve(jQuery.get('http://...')) // coerce any thenable
    .then(x => console.log(x)) //=> 123

fulfill :: a → Promise e a

Lift a value into a promise.

import { fulfill, resolve } from 'creed';

fulfill(123)
    .then(x => console.log(x)) //=> 123

// Note the difference from resolve
fulfill(fulfill(123))
    .then(x => console.log(x)) //=> '[object Promise { fulfilled: 123 }]'

resolve(fulfill(123))
    .then(x => console.log(x)) //=> 123

reject :: Error e => e → Promise e a

Make a rejected promise for an error.

import { reject } from 'creed';

reject(new TypeError('oops!'))
    .catch(e => console.log(e.message)) //=> oops!

never :: Promise e a

Make a promise that remains pending forever.

import { never } from 'creed';

never()
    .then(x => console.log(x)) // nothing logged, ever

Note: never consumes virtually no resources. It does not hold references to any functions passed to then, map, chain, etc.

Transform promises

.then :: Promise e a → (a → b|Promise e b) → Promise e b

Promises/A+ then. Transform a promise's value by applying a function to the promise's fulfillment value. Returns a new promise for the transformed result.

import { resolve } from 'creed';

resolve(1)
    .then(x => x + 1) // return a transformed value
    .then(y => console.log(y)) //=> 2

resolve(1)
    .then(x => resolve(x + 1)) // return transformed promise
    .then(y => console.log(y)) //=> 2

.catch :: Promise e a → (e → b|Promise e b) → Promise e b

Catch and handle a promise error.

import { reject, resolve } from 'creed';

reject(new Error('oops!'))
    .catch(e => 123) // recover by returning a new value
    .then(x => console.log(x)) //=> 123

reject(new Error('oops!'))
    .catch(e => resolve(123)) // recover by returning a promise
    .then(x => console.log(x)) //=> 123

.finally :: Promise e a → (() → b|Promise e b) → Promise e a

Perform cleanup side effects regardless of a Promise's outcome. If a finally handler:

  1. returns a non-promise, it is discarded
  2. returns a promise that fulfills, it's fulfillment value is discarded
  3. throws, the thrown error will take precedence
  4. returns a rejected promise, the rejection will take precedence
import { reject, resolve, delay } from 'creed'

resolve(123)
  .finally(() => resolve(456)) // do some cleanup
  .then(x => console.log(x)) //=> 123

reject(new Error('oops!'))
  .finally(() => resolve(456)) // do some cleanup
  .catch(e => console.log(e.message)) //=> oops!

reject(new Error('oops!'))
  .finally(() => delay(1000, 456)) // do some cleanup
  .catch(e => console.log(e.message)) //=> oops! after 1 second

As mentioned above, errors from a finally handler take precedence:

// Errors in finally handler take precedence
reject(new Error('oops!'))
  .finally(() => reject(new Error('finally error'))) // cleanup failed!
  .catch(e => console.log(e.message)) //=> finally error

reject(new Error('oops!'))
  .finally(() => {
    throw new Error('finally error') // cleanup failed!
  })
  .catch(e => console.log(e.message)) //=> finally error

.map :: Promise e a → (a → b) → Promise e b

Transform a promise's value by applying a function. The return value of the function will be used verbatim, even if it is a promise. Returns a new promise for the transformed value.

import { resolve } from 'creed';

resolve(1)
    .map(x => x + 1) // return a transformed value
    .then(y => console.log(y)) //=> 2

.bimap :: Promise e a → (e → f) → (a → b) → Promise f b

Fantasy-land Functor. Transform a promise's error or value by applying functions to either. The first function will be applied to the error of a rejected promise, and the second function will be applied to the value of a fulfilled promise. Like map, the return value of the applied function will be used verbatim, even if it is a promise. Returns a new promise for the transformed value.

import { resolve, reject } from 'creed';

resolve(1)
    .bimap(e => new Error('not called'), x => x + 1) // transform value
    .then(y => console.log(y)) //=> 2

reject(new Error('oops'))
    .bimap(e => new Error(e.message + '!!!'), x => x + 1) // transform error
    .catch(e => console.log(e)) //=> Error: oops!!!

.ap :: Promise e (a → b) → Promise e a → Promise e b

Apply a promised function to a promised value. Returns a new promise for the result.

import { resolve } from 'creed';

resolve(x => x + 1)
    .ap(resolve(123))
    .then(y => console.log(y)) //=> 124

resolve(x => y => x+y)
    .ap(resolve(1))
    .ap(resolve(123))
    .then(y => console.log(y)) //=> 124

.chain :: Promise e a → (a → Promise e b) → Promise e b

Sequence async actions. When a promise fulfills, run another async action and return a promise for its result.

let profileText = getUserProfileUrlFromDB(userId)
    .chain(fetchTextFromUrl)

profileText.then(text => console.log(text)) //=> <user profile text>

.or :: Promise e a → Promise e a → Promise e a

(deprecated) .concat :: Promise e a → Promise e a → Promise e a

Note: The name concat is deprecated, use or instead.

Returns a promise equivalent to the earlier of two promises. Preference is given to the callee promise in the case that both promises have already settled.

import { delay, fulfill } from 'creed';

delay(200, 'bar').or(delay(100, 'foo'))
    .then(x => console.log(x)); //=> 'foo'

fulfill(123).or(fulfill(456))
    .then(x => console.log(x)); //=> 123

Control time

delay :: Int → a|Promise e a → Promise e a

Create a delayed promise for a value, or further delay the fulfillment of an existing promise. Delay only delays fulfillment: it has no effect on rejected promises.

import { delay, reject } from 'creed';

delay(5000, 'hi')
    .then(x => console.log(x)) //=> 'hi' after 5 seconds

delay(5000, delay(1000, 'hi'))
    .then(x => console.log(x)) //=> 'hi' after 6 seconds

delay(5000, reject(new Error('oops')))
    .catch(e => console.log(e.message)) //=> 'oops' immediately

timeout :: Int → Promise e a → Promise e a

Create a promise that will reject after a specified time unless it settles earlier.

import { delay } from 'creed';

timeout(2000, delay(1000, 'hi'))
    .then(x => console.log(x)) //=> 'hi' after 1 second

timeout(1000, delay(2000, 'hi')) //=> TimeoutError after 1 second

Resolve Iterables

Creed's iterable functions accept any ES2015 Iterable. Most of the examples in this section show Arrays, but Sets, generators, etc. will work as well.

all :: Iterable (Promise e a) → Promise e [a]

Await all promises from an Iterable. Returns a promise that fulfills with an array containing all input promise fulfillment values, or rejects if at least one input promise rejects.

import { all, resolve } from 'creed';

all([resolve(123), resolve(456)])
    .then(x => console.log(x)) //=> [123, 456]

let promises = new Set()
promises.add(resolve(123))
promises.add(resolve(456))

all(promises)
    .then(x => console.log(x)) //=> [123, 456]

function *generator() {
    yield resolve(123)
    yield resolve(456)
}

all(generator())
    .then(x => console.log(x)) //=> [123, 456]

race :: Iterable (Promise e a) → Promise e a

Returns a promise equivalent to the input promise that settles earliest.

If there are input promises that are already settled or settle simultaneously, race prefers the one encountered first in the iteration order.

Note the differences from any().

Note: As per the ES6-spec, racing an empty iterable returns never()

import { race, resolve, reject, delay, isNever } from 'creed';

race([delay(100, 123), resolve(456)])
    .then(x => console.log(x)) //=> 456

race([resolve(123), reject(456)])
    .then(x => console.log(x)) //=> 123

race([delay(100, 123), reject(new Error('oops'))])
    .catch(e => console.log(e)) //=> [Error: oops]

isNever(race([])) //=> true

any :: Iterable (Promise e a) → Promise e a

Returns a promise equivalent to the input promise that fulfills earliest. If all input promises reject, the returned promise rejects.

If there are input promises that are already fulfilled or fulfill simultaneously, any prefers the one encountered first in the iteration order.

Note the differences from race().

import { any, resolve, reject, delay, isNever } from 'creed';

any([delay(100, 123), resolve(456)])
    .then(x => console.log(x)); //=> 456

any([resolve(123), reject(456)])
    .then(x => console.log(x)) //=> 123

any([reject(456), resolve(123)])
    .then(x => console.log(x)); //=> 123

any([delay(100, 123), reject(new Error('oops'))])
    .catch(e => console.log(e)); //=> 123

any([reject(new Error('foo')), reject(new Error('bar'))])
    .catch(e => console.log(e)) //=> [RangeError: No fulfilled promises in input]

any([])
    .catch(e => console.log(e)) //=> [RangeError: No fulfilled promises in input]

settle :: Iterable (Promise e a) → Promise e [Promise e a]

Returns a promise that fulfills with an array of settled promises.

import { settle, resolve, reject, isFulfilled, getValue } from 'creed';

// Find all the fulfilled promises in an iterable
settle([resolve(123), reject(new Error('oops')), resolve(456)])
    .map(settled => settled.filter(isFulfilled).map(getValue))
    .then(fulfilled => console.log(fulfilled)) //=> [ 123, 456 ]

Inspect

isFulfilled :: Promise e a → boolean

Returns true if the promise is fulfilled.

import { isFulfilled, resolve, reject, delay, never } from 'creed';

isFulfilled(resolve(123))        //=> true
isFulfilled(reject(new Error())) //=> false
isFulfilled(delay(0, 123))       //=> true
isFulfilled(delay(1, 123))       //=> false
isFulfilled(never())             //=> false

isRejected :: Promise e a → boolean

Returns true if the promise is rejected.

import { isRejected, resolve, reject, delay, never } from 'creed';

isRejected(resolve(123))        //=> false
isRejected(reject(new Error())) //=> true
isRejected(delay(0, 123))       //=> false
isRejected(delay(1, 123))       //=> false
isRejected(never())             //=> false

isSettled :: Promise e a → boolean

Returns true if the promise is either fulfilled or rejected.

import { isSettled, resolve, reject, delay, never } from 'creed';

isSettled(resolve(123))        //=> true
isSettled(reject(new Error())) //=> true
isSettled(delay(0, 123))       //=> true
isSettled(delay(1, 123))       //=> false
isSettled(never())             //=> false

isPending :: Promise e a → boolean

Returns true if the promise is pending (not yet fulfilled or rejected).

import { isPending, resolve, reject, delay, never } from 'creed';

isPending(resolve(123))        //=> false
isPending(reject(new Error())) //=> false
isPending(delay(0, 123))       //=> false
isPending(delay(1, 123))       //=> true
isPending(never())             //=> true

isNever :: Promise e a → boolean

Returns true if it is known that the promise will remain pending forever. In practice, this means that the promise is one that was returned by never() or a promise that has been resolved to such.

import { isNever, resolve, reject, delay, never, race } from 'creed';

isNever(resolve(123))         //=> false
isNever(reject(new Error()))  //=> false
isNever(delay(0, 123))        //=> false
isNever(delay(1, 123))        //=> false
isNever(never())              //=> true
isNever(resolve(never()))     //=> true
isNever(delay(1000, never())) //=> true
isNever(race([]))             //=> true

getValue :: Promise e a → a

Extract the value of a fulfilled promise. Throws if called on a pending or rejected promise, so check first with isFulfilled.

import { getValue, resolve, reject, never } from 'creed';

getValue(resolve(123)) //=> 123
getValue(reject())     //=> throws TypeError
getValue(never())      //=> throws TypeError

getReason :: Promise e a → e

Extract the reason of a rejected promise. Throws if called on a pending or fulfilled promise, so check first with isRejected.

import { getReason, resolve, reject, never } from 'creed';

getReason(resolve(123))      //=> throws TypeError
getReason(reject('because')) //=> 'because'
getReason(never())           //=> throws TypeError

Debugging

enableAsyncTraces :: () → ()

Enable async traces. Can be called at any time, but will only trace promises created after it's called. If called multiple times, resets the tracing state each time.

disableAsyncTraces :: () → ()

Disable async traces.

isHandled :: Promise e a → boolean

Returns true if the promise is rejected and the rejection has been "handled", that is, .catch has been called on the promise at least once with an argument that is a Function.

Note that if .catch is called with zero arguments or with an argument that isn't a Function, it does not affect the "handled" state of the promise.

Polyfill

shim :: () → PromiseConstructor|undefined

Polyfill the global Promise constructor with an ES6-compliant creed Promise. If there was a pre-existing global Promise, it is returned.

import { shim } from 'creed';

// Install creed's ES2015-compliant Promise as global
let NativePromise = shim()

// Create a creed promise
Promise.resolve(123)

Fantasy Land

Creed implements Fantasy Land 2.1:

creed's People

Contributors

bergus avatar briancavalier avatar davidchase avatar dmitriz avatar gitter-badger avatar greenkeeper[bot] avatar greenkeeperio-bot avatar unscriptable avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

creed's Issues

An in-range update of mocha is breaking the build 🚨

Version 3.4.0 of mocha just got published.

Branch Build failing 🚨
Dependency mocha
Current Version 3.3.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As mocha is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

Release Notes v3.4.0

Mocha is now moving to a quicker release schedule: when non-breaking changes are merged, a release should happen that week.

This week's highlights:

  • allowUncaught added to commandline as --allow-uncaught (and bugfixed)
  • warning-related Node flags

🎉 Enhancements

🐛 Fixes

🔩 Other

Commits

The new version differs by 9 commits0.

  • 7554b31 Add Changelog for v3.4.0
  • 9f7f7ed Add --trace-warnings flag
  • 92561c8 Add --no-warnings flag
  • ceee976 lint test/integration/fixtures/simple-reporter.js
  • dcfc094 Revert "use semistandard directly"
  • 93392dd no special case for macOS running Karma locally
  • 4d1d91d --allow-uncaught cli option
  • fb1e083 fix allowUncaught in browser
  • 4ed3fc5 Add license report and scan status

false

See the full diff

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

ES6 shim subclassability

Not sure to what extent this is desired.
Some problems with the ES6-like interface are:

  • methods (including static ones) don't return subclass instances
  • static methods don't use this to resolve their inputs (and don't require this to be a promise constructor at all)

We might want to put up a warning for this. Not sure how complicated or performance-deteriorating it would be if we wanted to support this.

Improve documentation and examples

From #178

  • Add simple "Introduction" and/or "Why would I use creed" sections
  • Add another 1-2 simple examples to the README.
  • Add links to helpful information about foundational functional programming concepts, such as the Mostly Adequate Guide.
  • More clearly encourage exploration via REPL.
  • Emphasize map, et al.
  • Group A+ / ES interop functions (i.e. then, resolve) together and explain more clearly that's their purpose. (#180)

Make never propagate

never() claims to "consume virtually no resources". However, this is no more true if never is used in any combinator, especially merge/all/settle. Should we detect nevers, and futures that eventually resolve to a never there and set the result accordingly?

If we want to do that, any ideas about the how? Should Action implement an onNever handler that is called by Never::_runAction?

Benefits over using Bluebird?

Hello,

As someone who is used to having Bluebird around, I'm wondering what creed brings over using BB?

The example converted to use Bluebird is pretty similar.

Perhaps I misunderstood and purpose of creed is only to augment native Promises with less powerful API than Bluebird's? I'd imagine it's less heavy at least, which could be a big deal in the browser side.

An in-range update of jsinspect is breaking the build 🚨

Version 0.12.6 of jsinspect just got published.

Branch Build failing 🚨
Dependency jsinspect
Current Version 0.12.5
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As jsinspect is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪

Status Details
  • continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details

Commits

The new version differs by 5 commits.

  • b49ec02 0.12.6
  • aeab96a Remove references to outdated plugins
  • 45837ce Undo the undo - enforce best practices and noisy output by ignoring tests
  • 1e6879c Performance improvement
  • 8c96921 Undo filtering of test and build files

See the full diff

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Any interest in TypeScript definitions?

Other than the lack of typing information, I find this to be my favorite promises (non-tasks/futures) library.

Would you be interested in trying to get type information added? Do you foresee any problems with typing certain things?

Consider integrating type class morphisms

See Conal Elliott's paper on denotational design and type class morphisms, or any of his recent talks based on it. One of the main points in the paper is that defining type class instances "can sometimes be derived mechanically from desired type class morphisms". Applying that to define, say, Promise's functor instance might go something like (using haskell-like syntax here, where fmap is the functor operation):

fmap f (Promise a) = Promise (fmap f a)

IOW, an fmapped future value is the future fmapped value. Or as it's stated in the paper: "The instance’s meaning follows the meaning’s instance"

Functor Example

That opens up some interesting possibilities .. for example:

fmap f (Promise [a]) = Promise (fmap f [a])

If we have a promise for an array of numbers and want add 1 to each number in the array, in creed 1.0, we have to map twice:

import { fulfill } from 'creed';
const ints = fulfill([1,2,3]);
const intsPlusOne = ints.map(a => a.map(x => x+1));

However, using the fmap definition above:

import { fulfill } from 'creed';
const ints = fulfill([1,2,3]);
const intsPlusOne = ints.map(x => x+1);

The exact same thing would work with a promise for a single number:

import { fulfill } from 'creed';
const one = fulfill(1);
const two = one.map(x => x+1);

Or for a promise for a tree of numbers, or a promise for a promise of a number, and so on. Applicative would likely be similar to Functor.

Questions

What about type classes for which there are two (or more) interesting behaviors? For example, creed's Promise has a monoid instance, with never as the identity element and concat returning the earlier of the two promises to settle. IOW, it doesn't rely on the monoid instance of the value type, but rather only on the time of the two promises.

That monoid definition is useful because it represents racing.

A monoid instance defined using the ideas in the paper would use the identity element of the value type (How would this even work??), and concat would apply the concat operation of the value type. IOW, concatenating two future arrays would produce a future concatenated array.

concat (Promise [a]) (Promise [a]) = Promise (concat [a] [a])

That's probably also useful, and I have no idea how to deal with wanting two monoid instances for Promise.

I also have no idea yet how to define a monad instance for Promise using this approach, if it's useful, or even possible. As far as I can tell, the paper doesn't much (if any) guidance on applying the technique for monad instances.

TypeError: Cannot read property 'near' of undefined

In v1.1.0 I'm seeing this error, but I don't see this error in v1.0.4 or v1.0.2. The specific area I'm seeing this is when using a coroutine, so creed.coroutine(generator)(val).then(x => x) is enough to trigger it for me.

An in-range update of eslint-plugin-standard is breaking the build 🚨

Version 2.2.0 of eslint-plugin-standard just got published.

Branch Build failing 🚨
Dependency eslint-plugin-standard
Current Version 2.1.1
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As eslint-plugin-standard is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪


Status Details
  • continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details
Commits

The new version differs by 3 commits .

See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

thisArg in merge - really necessary?

The merge callback f is called with the this of the merge(…) call as the receiver. This doesn't seem exactly useful to me.
Are there any use cases? Does any code use merge.call(o, f, …) instead of merge(f.bind(o), …)?
Could we just drop this "feature"?

Why there is no `creed.of` method?

The FL Applicative spec includes the of method but it does not seem to be available on creed:

> creed.of(1)
TypeError: creed.of is not a function

Any reason not to have it?

It could be mentioned that of is actually more basic than Applicative and is part of the Pointed Functor Spec, see also https://github.com/MostlyAdequate/mostly-adequate-guide-it/blob/master/ch9.md#pointy-functor-factory.

It seems that creed.fulfill is doing what of is meant to do,
which is somewhat non-standard name and is longer to write.
Also, when it is not called of, the question arises whether it conforms
to the Pointed Functor spec, which I understand it does.

If creed.fulfill is indeed intended to satisfy the Pointed Functor spec (together with map),
maybe also alias it as of and add tests for the spec?

Make never/empty a singleton

Is there any good reason why never() returns distinct objects?
Same could probably said for the internal Race handler.

Folding

From the Bifunctor, it seems a Promise has two states:

  1. Error value
  2. Successful value

I would like to "run" a Promise and act on each state:

/* forall x. */ Promise.of(x).fold(a => false, b => true) == true
/* forall x. */ reject(x).fold(a => true, b => false) == true
never.fold(a => true, b => true) == undefined

An in-range update of nyc is breaking the build 🚨

Version 10.3.0 of nyc just got published.

Branch Build failing 🚨
Dependency nyc
Current Version 10.2.2
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As nyc is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪

Status Details - ❌ **coverage/coveralls** Coverage pending from Coveralls.io [Details](https://coveralls.io/builds/11301042),- ❌ **continuous-integration/travis-ci/push** The Travis CI build failed [Details](https://travis-ci.org/briancavalier/creed/builds/227079149?utm_source=github_status&utm_medium=notification)

Commits

The new version differs by 4 commits ahead by 4, behind by 2.

  • 55e826d chore(release): 10.3.0
  • 89dc7a6 chore: explicit update of istanbul dependnecies (#562)
  • 1887d1c feat: add support for --no-clean, to disable deleting raw coverage output (#558)
  • ff73b18 fix: source-maps were not being cached in the parent process when --all was being used (#556)

false

See the full diff

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Overhaul rejection tracking

Currently error reporting does keep track of every Rejected instance.
ES7 promises (and afaik basically any other promise implementations) do however keep track of the promises (futures) that are rejected without handlers.
See tc39/ecma262#76 for more links.

The problem with Creed's current approach is that it does not detect branching. Consider the example

const p = reject();
const q = p.then();
const r = p.catch();

While Creed does report p to be handled, it should actually have reported q as unhandled in the first place.

This is not an easy problem for sure with the nearing approach, as with x = reject(…), y = new Future, z = new Future we would have to distinguish z.become(y); y.become(x) from z.become(x); y.become(x) (in any order).

Any thoughts?


I am not sure to what extent creed does use the standardised rejection tracking events, I kinda have lost track of proposals/implementations/specifications.
Btw, node has some nice tests here.
Also this issue will probably solve the matter of silencer being an Action, I'd expect silencing to see some major change.

Implement Cancellation

Hi,
I'd love to implement promise cancellation in Creed, using the approach detailed in tc39/proposal-cancelable-promises#19. I want to do this as a proof of concept and just for fun, and chose your library for its readable source and large test-suite.

Would you like to see this as well? If no, I'll just maintain my own fork. If yes, how would you like the workflow to look like (feature-branches, how many pull requests, etc)?

Do you have any implementation tips? Any performance secrets? How would I best ask questions and get feedback from you?

Let's discuss technical details later.

more accessible documentation?

I have trouble taking native/A+ promises seriously, mostly due to its impossible-to-analyze behavior around automagically unwrapping thenables (I believe this is commonly known as "failure to be a monad").

As I consider long-term solutions, alternatives like fantasy-land and creed::async become quite attractive.
But, frustratingly, my several attempts to dive into either one of them have failed, due to my failure to being able to follow the documentation of either, to any significant depth. I also have the feeling that, even if I succeed in vaulting the very high bar to entry, I will leave my normal friends and coworkers behind, since they will be unable to ever read my code from then on.

  • The fantasy-land doc dives straight into algebraic "abstract nonsense" (I don't neceessarily use the term dismissively, but it does make that doc impenetrable and thus not useful to me for learning).
  • fluture-js/Fluture might be a decent alternative; I'll explore that in parallel.

This creed::async certainly sounds great, from its introductory blurb (great marketing!), so I'd really like to try it, so that's what brings me here.

As I continue reading the README, I encounter:

  • The async-problem example. Ok, I guess it solves this puzzle neatly. Can't really make head nor tail of the details at this point, and it's doesn't seem that relevant to my wish to learn the library.
  • The "Try it" section. Ok, I follow this section: it looks just like the Promises I know, so far, but there's not a lot in this section.
  • ... scrolling down, hoping to see some more introductory examples of how to use various features, and I reach:
  • API : Run async tasks: coroutine :: Generator -> (...* --> Promise e a) : Create an async coroutine from a promise-yielding generator. [... example that doesn't seem relevant to me yet ...] WHOA! I suddenly feel very far out of my depth...
  • fromNode :: NodeApi e a --> (...* --> Promise e a) [... more stuff that doesn't seem relevant to me at all ...]
  • scrolling down more, I see lots more API reference and code examples, but they all seem to assume I already know what's going on. I'm lacking any context or feeling like I know how to get my foot in the door.

What I'm trying to say is... is there any chance of getting some introductory material in there,
to bridge the large gap between the shallow "Try It" section and the deep "API" section, for programmers like me who want to learn this library?

In particular, I'd be interested in some dedicated material explaining, in both plain language and code:

  • A list of each the async constructs/styles that creed::async provides, with high-level description of pros and cons of each compared to each other.
  • Compare/contrast these constructs/styles with native/A+ promises and native async/await, point out the differences / added-value that are the reason a reasonable person would want to use this library.
  • Maybe also compare/contrast with other alternative libraries like Fluture? I actually don't know how you view creed::async in relation to other alternatives at this point, and it would be good to know.
  • Then ease into more depth, with some well-motivated examples of each construct/style without assumption of prior knowledge, with explanations and code comments less terse than the API examples.
  • My impression, from reading your and others' comments in other discussion threads that lead here, is that a crucial motivating point about this library is something like "creed::async::Promise is A+ compliant since it has a compliant then, but if you want something with analyzable semantics, don't use that, use its 'chain' instead". But I don't see that stated or even alluded to anywhere in the creed::async doc! (And, truthfully, I'm not even sure I have it straight.) I feel like there may be a lot of other important context and motivation like this that I'm missing, too. A dedicated "Motivation" section might go a long way.

A final note-- personally, I know Promises/A+ already, so I don't mind if the doc assumes that knowledge, for my own use in learning the library. However, if this library is intended to be a serious option for the world for doing async, it would be great if there were also an introduction from first principles for javascript programmers, so that people can skip A+ and get right to the sane stuff. That would be a more ambitious undertaking, though.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.