A solid, fast Promises/A+ and when() implementation, plus other async goodies.

Related tags

Control Flow when
Overview

Promises/A+ logo

Build Status Inline docs

when.js

When.js is a rock solid, battle-tested Promises/A+ and when() implementation, including a complete ES6 Promise shim. It's a powerful combination of small size, high performance, debuggability, and rich features:

  • Resolve arrays and hashes of promises, as well as infinite promise sequences
  • Execute tasks in parallel or sequentially
  • Transform Node-style and other callback-based APIs into promise-based APIs

When.js is one of the many stand-alone components of cujoJS, the JavaScript Architectural Toolkit.

Check it out:

Installation

AMD

Available as when through bower, or just clone the repo and load when.js from the root.

bower install --save when

CommonJS/Node

npm install --save when

More help & other environments »

Usage

Promises can be used to help manage complex and/or nested callback flows in a simple manner. To get a better handle on how promise flows look and how they can be helpful, there are a couple examples below (using commonjs).

This first example will print "hello world!!!!" if all went well, or "drat!" if there was a problem. It also uses rest to make an ajax request to a (fictional) external service.

var rest = require('rest');

fetchRemoteGreeting()
    .then(addExclamation)
    .catch(handleError)
    .done(function(greeting) {
        console.log(greeting);
    });

function fetchRemoteGreeting() {
    // convert native Promise to a when.js promise for 'hello world'
    var result = rest('http://example.com/greeting');
    return when(result)
}

function addExclamation(greeting) {
    return greeting + '!!!!'
}

function handleError(e) {
    return 'drat!';
}

The second example shows off the power that comes with when's promise logic. Here, we get an array of numbers from a remote source and reduce them. The example will print 150 if all went well, and if there was a problem will print a full stack trace.

var when = require('when');
var rest = require('rest');

when.reduce(when.map(getRemoteNumberList(), times10), sum)
    .done(function(result) {
        console.log(result);
    });

function getRemoteNumberList() {
    // Get a remote array [1, 2, 3, 4, 5]
    return rest('http://example.com/numbers').then(JSON.parse);
}

function sum(x, y) { return x + y; }
function times10(x) {return x * 10; }

License

Licensed under MIT. Full license here »

Contributing

Please see the contributing guide for more information on running tests, opening issues, and contributing code to the project.

References

Much of this code was inspired by the async innards of wire.js, and has been influenced by the great work in Q, Dojo's Deferred, and uber.js.

Comments
  • Latest build and issues with promise joining

    Latest build and issues with promise joining

    we've recently upgraded in the latest build of when and we've started to some sporadic errors from when that were not getting before. here is a part of a stack:

    TypeError: object is not a function
       at Promise._beget (/usr/hs/release/amber/amber/node_modules/bootstrap/node_modules/when/lib/makePromise.js:163:16)
       at Promise.then (/usr/hs/release/amber/amber/node_modules/bootstrap/node_modules/when/lib/makePromise.js:137:17)
       at Promise.catch (/usr/hs/release/amber/amber/node_modules/bootstrap/node_modules/when/lib/makePromise.js:153:16)
       at Promise.catch.Promise.otherwise (/usr/hs/release/amber/amber/node_modules/bootstrap/node_modules/when/lib/decorators/flow.js:36:22)
    

    its not happening all of the time and it's happening in different places in our code. it was not happening before we upgraded.

    opened by jefflage 113
  • Make always() more sane

    Make always() more sane

    Right now, promise.always() is weird for a few reasons, several of which were discussed in this google group thread. For instance:

    1. It receives either a fulfilled value, or a rejection reason and can't easily distinguish, except by inspecting the value/reason itself. This makes the function passed to always() trickier to implement and potentially more error prone.
    2. It is easy to accidentally "handle" a rejection simply by returning. For example, passing the identity function or a function that returns undefined, like console.log to always() will always squelch errors.

    One idea would be to try to make always() work more like synchronous finally. Consider the following code:

    try {
      return doSomething(x);
    } catch(e) {
        return handleError(e);
    } finally {
        cleanup();
    }
    

    Notice that this finally clause does not have access to the return value unless we do some extra work above and capture it in a variable. So, the "default" is for finally NOT to have access to the return value or to the exception. While cleanup can throw an exception, thus transforming an originally-successful return into a failure, it cannot turn a failure into a successful return by not throwing.

    Now consider how the current always() implementation works in this code:

    var fn = require('when/function');
    fn.call(doSomething, x)
      .otherwise(handleError)
      .always(cleanup);
    

    There are two obvious differences:

    1. cleanup has access to the result or the error.
    2. cleanup can modify the outcome of the operation in a way that it can't above: it can transform a failure into success simply by returning (even if it simply returns its input, i.e. the identity function!).

    Here is a proposed version of always that I think behaves more like (but still not exactly like) finally:

    Promise.prototype.always = function(callback) {
      return this.then(
        function(value) {
          return when(callback(), function() {
            return value;
          });
        },
        function(error) {
          return when(callback(), function() {
            return when.reject(error)
          });
        });
      )
    }
    

    Notice that callback is not given access to either the fulfillment value or the rejection reason. It can still turn a success into a failure by throwing, but it cannot turn a failure into a success simply by returning. It also cannot change the ultimate fulfillment value.

    I'm becoming a fan of this, but want to see what other folks think.

    One potential variant of this would be to pass the value/reason to callback, but still to disallow the case of turning a failure into success. That would look like:

    Promise.prototype.always = function(callback) {
      return this.then(
        function(value) {
          return when(callback(value), function() {
            return value;
          });
        },
        function(error) {
          return when(callback(error), function() {
            return when.reject(error)
          });
        });
      )
    }
    

    Thoughts? Alternatives?

    opened by briancavalier 56
  • IE8 performance issue

    IE8 performance issue

    Hi Brian,

    We are experiencing extreme performance issues with when 2.6.0 (or any 2.x version).

    I have created a repo: https://github.com/jbadeau/when-2.x-ie8-performance

    which demonstrates the issue.

    basically when 2.x is used the app leaks about 20mb on refresh and load time is heavily impacted. IE8 becomes completely unusable in a moderately complex wire app in about 3 refreshes.

    I hate having to support IE8 but I cant get rid of it yet :(

    This is a very critical issue for us. If you need any additional info or help please let me know.

    Cheers, Jose

    opened by jbadeau 40
  • when.always([], alwaysFunction) ?

    when.always([], alwaysFunction) ?

    I have a use case when composite promise should always resolve and return an array of rejected or resolved results. I do not really care of the results, but I must synchronize my promises.

    function randomReject() {
        var dfd = when.defer(),
            rnd = Math.random();
    
        setTimeout(function () {
            if (rnd > 0.5) {
                console.log('dfd.resolve');
                dfd.resolve(rnd);
            } else {
                console.log('dfd.reject');
                dfd.reject(new Error(rnd));
            }
        }, rnd * 1000);
    
        return dfd.promise;
    }
    
    var promises = [randomReject(), randomReject(), randomReject(), randomReject()];
    
    // (!) Note: This is prototype. It is out of code style
    when.always = function (promises, onResolve) {
        promises = promises.map(function (promise) {
            var dfd = when.defer();
    
            promise.always(dfd.resolve);
    
            return dfd.promise; 
        });
    
        return when.all(promises, onResolve);
    }
    
    when.always(promises, function () {
        // if (arguments[i] instanceof Error) do stuff();
        console.log('OK', arguments);
    });
    

    http://jsfiddle.net/

    Should I implement when.always([], alwaysFunction) in addition to promise.always(alwaysFunction); ?

    opened by azproduction 40
  • Refine monitor reporting API

    Refine monitor reporting API

    In 3.0, the monitor is getting a total rewrite. This is a good time to figure out what the right API would be to make it easy for developers to hook their own reporter into the monitor. See this discussion over in #254 for a start.

    3.x 
    opened by briancavalier 33
  • forEach() for sequence iterable and array promise subtypes

    forEach() for sequence iterable and array promise subtypes

    It has it's uses, especially in the case of iterable promise subtypes where it can allow the caller to consume the items in the iterator individually vs. reduce, which consumes all items to produce a summary.

    Another difference could be that we could allow array forEach to be parallelized, like map, versus reduce, which much be serial.

    when3 
    opened by briancavalier 31
  • Add convenience methods like .always()

    Add convenience methods like .always()

    Currently, when.js's promise provides .then(), so if you want to register the same callback to run whether the promise is resolved or rejected, you have to pass it explicitly as both the callback and errback to when() or .then(). Both jQuery Deferred, and Q's promises provide convenience methods for this.

    Here's how you have to do it with when.js currently:

    promise.then(callback, callback);
    // or
    when(promise, callback, callback);
    

    That's not horrible, but this seems like a nice convenience:

    promise.always(callback);
    // or
    when.always(promise, callback);
    

    It might be interesting to provide both promise.always(callback) and when.always(promise, callback) so that when.always() can be used with other promise implementations. Also, since something like promise.always() is not part of the Promises/A spec, relying on it would mean tying your code to when.js's promise implementation. Using when.always(promise) is a bit more decoupled.

    Seems like this also opens the door for a method that registers only a rejection handler. For example, to do that now:

    promise.then(null, errback);
    // or
    when(promise, null, errback);
    

    Which is kind of ugly, and might cause a "WTF". So, could provide something like:

    promise.rejected(errback);
    // or
    when.rejected(promise, errback);
    

    This seems like a much more limited use case to me, though.

    opened by briancavalier 31
  • TypeError: object is not a function

    TypeError: object is not a function

    Starting with 3.6.0, but possibly even before that, there is a TypeError: object is not a function error thrown at seemingly random situations, as also discussed in #345.

    The following coffee code manages to reproduce it: it fails with [email protected] running on node v0.10.33/coffeescript 1.8, on both Ubuntu 14x64 & Windows 7x64 after exactly 393 overall iterations :-/

    The error is very strangely random-y: although this code fails predictably (at overall iteration noted as registering catch # 393), almost any change affects the error and might not even throw at all or throw longer after - even a console.log change, or the useless loops and promises formation etc might affect the outcome...

    I highly sympathize whoever is going to debug this unpredictable ghost busting monster ;-(

    fs = require 'fs'
    
    When = require 'when'
    When.node = require 'when/node'
    When.sequence = require 'when/sequence'
    readFileP = When.node.lift fs.readFile
    
    path = require 'path'
    expand = require 'glob-expand'
    esprima = require 'esprima'
    
    jspath = '../node_modules/lodash'
    
    arrayItems = []
    for i in [1..1000]
      arrayItems.push do (i)-> -> When(i)
    
    fileTxt = null
    
    files = expand {cwd: jspath, filter: 'isFile'}, ['**/*.js']
    for file in files
      fileTxt = fs.readFileSync(path.join(jspath, file), 'utf8')
      esprima.parse(fileTxt)
      console.log "test parsing file is ok `#{file}`"
    
    catchCount = 0
    When.iterate(
      (i)-> i + 1
      (i)-> !(i < arrayItems.length)
      (i)->
        item = arrayItems[i]
    
        When.iterate(
          (j)-> j + 1
          (j)-> !(j < files.length)
          (j)->
            file = files[j]
            p = When.sequence([
              ->
                item().then (v)-> When(v).delay(1).then (v)->
                  console.log "##{v} reading file with promise: `#{file}`"
                  readFileP(path.join(jspath, file), 'utf8').then (res)->
                    fileTxt = res
    
              ->
                item().then (v)-> When(v).delay(1).then (v)->
                  console.log "##{v} parsing file: `#{file}`"
                  for i in [1..10]
                    ast = esprima.parse(fileTxt)
                  console.log fileTxt[1..100], ast.type
            ])
    
            console.log 'registering catch #', catchCount++
            p.catch (err)->
              l.error "An error was caught:", err
              process.exit 1
        ,0)
    ,0)
    

    The last lines of the outcome are

    ....
    #56 reading file with promise: `lodash.js`
    #56 parsing file: `lodash.js`
    **
     * @license
     * Lo-Dash 2.4.1 <http://lodash.com/>
     * Copyright 2012-2013 The Dojo Foundation <htt Program
     registering catch # 392
    #57 reading file with promise: `dist/lodash.compat.js`
    #57 parsing file: `dist/lodash.compat.js`
    **
     * @license
     * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
     * Build: `lodash -o ./dist/loda Program
     registering catch # 393
    Potentially unhandled rejection [1] TypeError: object is not a function
      at Promise._beget (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\makePromise.js:166:16)
      at Promise.then (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\makePromise.js:140:17)
      at Promise.catch (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\makePromise.js:156:16)
      at Promise.catch.Promise.otherwise (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\decorators\flow.js:37:22)
      at E:\dev\uBerscore\node_modules\urequire\DRAFT\whentest.coffee:58:13
      at next (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\decorators\iterate.js:57:20)
      at E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\decorators\array.js:35:24
      at tryCatchReject (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\makePromise.js:830:30)
      at runContinuation1 (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\makePromise.js:789:4)
      at Fulfilled.when (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\makePromise.js:580:4)
      at Pending.run (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\makePromise.js:471:13)
      at Scheduler._drain (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\Scheduler.js:62:19)
      at Scheduler.drain (E:\dev\uBerscore\node_modules\urequire\node_modules\when\lib\Scheduler.js:27:9)
      at process._tickCallback (node.js:419:13)
    
    #57 reading file with promise: `dist/lodash.compat.min.js`
    #57 parsing file: `dist/lodash.compat.min.js`
    **
     * @license
     * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs Program 
    
    opened by anodynos 30
  • Adding a .bind method similar to bluebird

    Adding a .bind method similar to bluebird

    The lack of a useful this is one of the main issues I have with promises. While using Function.prototype.bind works just fine, it gets a little annoying when you need to do it all over the place for every method...

    It'd be nice if there was something similar to the .bind method in bluebird, where you could have:

    when.bind(this)
      .then(this.someMethod)
      .then(this.someOtherMethod)
      .otherwise(this.someErrorHandler)
    

    vs.

    when
      .then(this.someMethod.bind(this))
      .then(this.someOtherMethod.bind(this))
      .otherwise(this.someErrorHandler.bind(this))
    

    and be assured that this is bound for each step, rather than needing to bind each fulfilled/rejected handler individually.

    when3 
    opened by tgriesser 29
  • Shorthand function for 'converting' callback-based APIs

    Shorthand function for 'converting' callback-based APIs

    Hi! I was thinking if it wouldn't be useful to have a shorthand function to abstract this common pattern (at least from my experience)

    // Using jQuery's domready as an example
    
    var domReadyPromise = (function() {
      var deferred = when.defer();
      $(function() { deferred.resolve(this) });
      return deferred.promise;
    })();
    

    Into something like this (the name is not very good)

    var domReadyPromise = when.callback($);
    

    Although I'm mostly a browser guy, I think it should be useful for dealing with Node's callback-based APIs as well.

    It seems to be simple enough for me to implement (yay!). I could make a PR if you guys think it would be a good to have.

    Thanks!

    opened by renato-zannon 29
  • Closure Compiler compatibility

    Closure Compiler compatibility

    Hi, it is mentioned in the docs that the code has been minified using CC in ADVANCED_OPTIMIZATIONS.

    Is there an exports file where all the exported symbols are declared?

    I plan on using when.js in a closure lib project and i am trying to figure out the best strategy to integrate it, using an externs file or directly annotating the codebase.

    Would you be interested in work that makes when.js CC compatible?

    opened by thanpolas 28
  • npm install failing with 404 error code

    npm install failing with 404 error code

    I am trying to build [email protected] locally. When I try to run "npm install" it fails with the following error:

    npm ERR! 404 Not Found - GET https://github.com/cujojs/when/tarball/1.8.1

    opened by varunmankal 0
  • Resolving vertx dependency issues with webpack

    Resolving vertx dependency issues with webpack

    What I’ve changed: In /lib/env.js, the vertx require statement now uses @vertx/core, which is the node package for vertx. This resolves an issue when using webpack (see #482) package.json has also been updated to include @vertx/core as a dependency.

    opened by trylaarsdam 1
  • Fix custom inspection in new Node.js versions

    Fix custom inspection in new Node.js versions

    Newer Node.js versions export a special symbol that should be used to custom inspect objects. Otherwise the object itself would be poluted. This adds the symbol in the most backwards compatible way possible: it prevents any deprecation message from showing up and behaves just as before. Otherwise the custom inspection will break in Node.js 11.

    I am not sure if it is the best way to tackle this, so I did not add any tests.

    opened by BridgeAR 0
  • call the catch handler only if it is a function

    call the catch handler only if it is a function

    Hello,

    You should include the type check on catch handler because if a handler is not a function, the catch method throws the TypeError

    TypeError: handler.call is not a function

    For example, check the following code:

    var p1 = Promise.resolve(18);
    var p2 = Promise.reject(17);
    p2.catch(function(){
    return p1;
    }, p1);
    

    The second argument is non-function value and in this case the TypeError is thrown becuase method call can't be called on a non-function object.

    opened by marijaselakovic 1
  • RTE TypeError after production build

    RTE TypeError after production build

    Using angular5 and the autobahn package, building the app with angular cli (ng build --prod)

    Uncaught TypeError: (intermediate value)(intermediate value) is not a function at when.js:1008 at Object.12.../apply (when.js:753) at o (when.js:13) at when.js:13 at when.js:2948 at when.js:3133 at Object.32../lib/Promise (when.js:2944) at o (when.js:13) at when.js:13 at Object.1.../callbacks (when.js:16)

    opened by filipefreitas82 4
Releases(3.7.8)
Owner
The Javascript Architectural Toolkit
The Javascript Architectural Toolkit
🚀 Tiny goodies for Continuation-Passing-Style functions, fully tested

// ) ) ___ ___ ___ __//__ // ) ) // ) ) (( ) ) // // / / // //___/ / \ \ // ((___/ / ((___

Dmitri Zaitsev 64 Nov 20, 2022
Helps you write libraries that accept both promises and callbacks.

What is it? promise-breaker makes it easy to write functions that will accept an optional callback, or return a Promise if a callback is not provided.

Jason Walton 83 Aug 11, 2022
The ultimate generator based flow-control goodness for nodejs (supports thunks, promises, etc)

co Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way. Co v4 co@4

TJ Holowaychuk 11.8k Jan 2, 2023
Map over promises concurrently

p-map Map over promises concurrently Useful when you need to run promise-returning & async functions multiple times with different inputs concurrently

Sindre Sorhus 929 Dec 31, 2022
P - Toolkit for managing multiple promises

@antfu/p Toolkit for managing multiple promises. Without const items = [1, 2, 3, 4, 5] (await Promise.all(items .map(async i => { const v = awa

Anthony Fu 284 Nov 24, 2022
Async utilities for node and the browser

Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript. Although originally designed f

Caolan McMahon 27.8k Dec 31, 2022
An async control-flow library that makes stepping through logic easy.

Step A simple control-flow library for node.JS that makes parallel execution, serial execution, and error handling painless. How to install Simply cop

Tim Caswell 2.2k Dec 22, 2022
CSP channels for Javascript (like Clojurescript's core.async, or Go) THIS IS AN UPSTREAM FORK

js-csp Communicating sequential processes for Javascript (like Clojurescript core.async, or Go). Examples var csp = require("js-csp"); Pingpong (porte

James Long 283 Sep 22, 2022
An extension to Async adding better handling of mixed Series / Parallel tasks via object chaining

async-chainable Flow control for NodeJS applications. This builds on the foundations of the Async library while adding better handling of mixed Series

Matt Carter 25 Jul 15, 2019
An Implementation of Observables for Javascript

zen-observable An implementation of Observables for JavaScript. Requires Promises or a Promise polyfill. Install npm install zen-observable Usage impo

Kevin Smith 839 Dec 21, 2022
Flow control and error handling for Node.js

NOTE: This project is deprecated and no longer being actively developed or maintained. See Issue #50 for details. StrongLoop zone library Overview The

StrongLoop and IBM API Connect 280 Feb 18, 2022
Memoize promise-returning functions. Includes cache expire and prefetch.

promise-memoize Memoize promise-returning functions. Includes cache expire and prefetch. When data expire mode enabled, new values are fetched in adva

Nodeca 56 Nov 1, 2022
A SolidJS starter template with solid-labels, solid-sfc and solid-styled

solid-sfc-styled-labels-starter This is a SolidJS starter template for easily setting up solid-sfc, solid-styled and solid-labels. Development Install

Alexis H. Munsayac 9 Mar 25, 2022
Kysely dialects, plugins and other goodies for SurrealDB

kysely-surrealdb Kysely dialects, plugins and other goodies for SurrealDB. SurrealQL is based on SQL, so why not? Installation NPM 7+ npm i kysely-sur

Igal Klebanov 16 Jan 6, 2023
Run async code one after another by scheduling promises.

promise-scheduler Run async code in a synchronous order by scheduling promises, with the possibility to cancel pending or active tasks. Optimized for

Matthias 2 Dec 17, 2021
Converts an iterable, iterable of Promises, or async iterable into a Promise of an Array.

iterate-all A utility function that converts any of these: Iterable<T> Iterable<Promise<T>> AsyncIterable<T> AsyncIterable<Promise<T>> Into this: Prom

Lily Scott 8 Jun 7, 2022
Async concurrent iterator (async forEach)

each-async Async concurrent iterator (async forEach) Like async.each(), but tiny. I often use async.each() for doing async operations when iterating,

Sindre Sorhus 107 Oct 21, 2022
A workshop about JavaScript iteration protocols: iterator, iterable, async iterator, async iterable

JavaScript Iteration protocol workshop A workshop about JavaScript iteration protocols: iterator, iterable, async iterator, async iterable by @loige.

Luciano Mammino 96 Dec 20, 2022
Plain functions for a more functional Deku approach to creating stateless React components, with functional goodies such as compose, memoize, etc... for free.

"Keo" is the Vietnamese translation for glue. Plain functions for a more functional Deku approach to creating stateless React components, with functio

Adam Timberlake 225 Sep 24, 2022