SkillAgentSearch skills...

Creed

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

Install / Use

/learn @briancavalier/Creed
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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.

<a href="http://promises-aplus.github.com/promises-spec"><img width="82" height="82" alt="Promises/A+" src="http://promises-aplus.github.com/promises-spec/assets/logo-small.png"></a> <a href="https://github.com/fantasyland/fantasy-land"><img width="82" height="82" alt="Fantasy Land" src="https://raw.github.com/puffnfresh/fantasy-land/master/logo.png"></a> 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) {
	// Imple

Related Skills

View on GitHub
GitHub Stars278
CategoryDevelopment
Updated3mo ago
Forks20

Languages

JavaScript

Security Score

97/100

Audited on Dec 2, 2025

No findings