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 🙏

© 2025 – Pkg Stats / Ryan Hefner

sortasync

v0.2.1

Published

A promise-powered utility for interdependent async calls

Readme

Sortasync

A promise-powered utility for interdependent async calls.

What is it?

For one-off async operations, promises are great. For uniform collections Neo-Async is your best friend. But when it comes to complex, interdependent sequences of async calls, it's still difficult to achieve maximum concurrency without a mess of callbacks.

Sortasync solves this problem by blending the best ideas from multiple sources. Features include:

  • Super flat, dead simple definition syntax
  • Angular-style implicit dependency injection
  • Eats and returns promises (from whatever A+ library you throw at it)
  • Available for Node, AMD, and the browser

Basic example

In this contrived example, we fetch relevant information for a user from multiple sources.

var mySequence = new Sortasync({
  user: function() {
    return someApi.getCurrentUser();
  },
  group: function(user) {
    return someApi.getGroupByUser(user.id);
  },
  friends: function(user) {
    return someApi.getFriendsByUser(user.id);
  },
  page: function(user) {
    return someApi.getFriendsByUser(user.id);
  },
  comments: function(page) {
    return someApi.getCommentsByPage(page.id);
  },
  likes: function(page) {
    return someApi.getLikesByPage(page.id);
  }
});

mySequence
  .exec()
  .then(function(response) {
    doSomethingWith(response.user);
    doSomethingWith(response.group);
    doSomethingWith(response.friends);
    doSomethingWith(response.page);
    doSomethingWith(response.comments);
    doSomethingWith(response.likes);
  }));

In the example above, group, friends, and page all depend on the resolved value of the user operation. They will fire concurrently as soon as user is resolved.

Likewise, comments and likes depend on the resolved value of the page operation, so they will await that resolution and then fire concurrently.

The operations above are written in order for clarity, but this is not required (as objects have no concept of "order" anyway).

Kickstart with arguments

Your Sortasync instance can be reused, and you may want to pass it arguments in order to kickstart the sequence. You can use the special _args key to name these arguments ahead of time, then inject them as usual.

var mySequence = new Sortasync({
  _args: [ 'userId', 'accessToken' ],
  user: function(userId, accessToken) {
    return someApi.getUser(userId, accessToken);
  }
});

mySequence
  .exec(123, VERY_SECURE_TOKEN)
  .then(...)

Rejections and exceptions

Under the hood Sortasync constructs one unbroken chain of promises using Promise.all, so a rejection or error at any step of the process will travel down the chain and result in a rejection of the final returned promise.

If a promise was "intentionally rejected" (rejected with a value that is not an instance of Error), Sortasync will normalize the rejection as an Error object with the name RejectionError. If the rejection value is a string, that string will be used as the error's message. If the rejection arises from an exception, Sortasync will pass on the original Error.

In both cases, Sortasync will decorate the Error object with a sortasync key that contains additional data about the rejection.

Intentional rejection:

var mySequence = new Sortasync({
  wontWork: function() {
    return Promise.reject('Computer says no');
  }
});

mySequence
  .exec()
  .catch(function(err) {
    err instanceof Error; // true
    err.name;             // 'RejectionError'
    err.message;          // 'Computer says no' (rejection value if string, otherwise '')
    err.sortasync.step;   // 'wontWork'
    err.sortasync.reason; // 'Computer says no' (rejection value regardless of type)
  }));

Exceptional rejection:

var mySequence = new Sortasync({
  wontWork: function() {
    return someUndefinedFunction();
  }
});

mySequence
  .exec()
  .catch(function(err) {
    err instanceof Error; // true
    err.name;             // 'ReferenceError'
    err.message           // 'someUndefinedFunction is not defined'
    err.sortasync.step;   // 'wontWork'
    err.sortasync.reason; // null
  }));

Customize rejection behavior

If you'd like to customize how rejections and exceptions are handled, just replace normalizeRejection on the prototype.

This code runs in a catch block attached to each individual step in the Sortasync sequence (so as to capture the offending step), so if you want the rejection to cascade to the final promise, be sure to re-emit a rejection or error of some kind. Not doing so will effectively "squash" the error and the key will resolve to undefined in the final payload.

Sortasync.prototype.normalizeRejection = function(rejectionValue, stepName) {
  if (typeof rejectionValue === 'string') {
    var cameFromAbove = rejectionValue.match(/^Something went wrong at (.*)$/);
    stepName = cameFromAbove ? cameFromAbove[1] : stepName;
  }
  return Promise.reject("Something went wrong at " + stepName);
}

Explicit dependency injection

Sortasync supports explicit dependency injection for code that will be minified.

var mySequence = new Sortasync({
  _args: [ 'arg1', 'arg2' ],
  firstCall: [ function() {
    return someApi.doSomething();
  }],
  secondCall: [ 'firstCall', function(firstCall) {
    return someApi.doSomethingWith(firstCall);
  }]
});

Builds

The final distributed library comes in three flavors:

sortasync.bundle.js

Sortasync for CommonJS, AMD, and the browser (global Sortasync) bundled with the Babel Runtime Transform. This plug-and-play version includes polyfills for Promise and Map without polluting your global namespace. This is the version you'll receive when you require('sortasync'). 9kb minified and gzipped.

sortasync.compat.js

Sortasync for CommonJS, AMD, and the browser with ES2015 language features replaced but Promise and Map intact. Legacy environments will require a polyfill for these. 1kb minified and gzipped.

sortasync.es2015.js

Sortasync au naturel: CommonJS, ES2015. Use with Node 6+. 1kb gzipped.

About Classy

Classy is a technology company that helps organizations mobilize their community to solve social problems more effectively and efficiently. Since launching in 2011, Classy has helped more than 2,500 social enterprises including Oxfam, The World Food Programme and National Geographic to raise hundreds of millions of dollars. Classy also hosts the Collaborative and Classy Awards to recognize excellence in social innovation. Based in San Diego, CA, Classy employs a staff of 165 people and is backed by investors including Mithril and Salesforce Ventures.

Learn more: https://www.classy.org

And, we're hiring: http://www.classy.org/careers