Skip to content
/ yaku Public
forked from ysmood/yaku

An ES6 Promise/A+ implementation that doesn't hurt.

License

Notifications You must be signed in to change notification settings

2hu12/yaku

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Promises/A+ logo

Overview

Yaku is full compatible with ES6's native Promise, but much faster.

If you want to learn how Promise works, read the minimum implementation docs/minPromiseA+.coffee. Without comments, it is only 80 lines of code. It only implements the constructor and then. It passed all the tests of promises-aplus-tests.

I am not an optimization freak, I try to keep the source code readable and maintainable. Premature optimization is the root of all evil. I write this lib to research one of my data structure ideas: docs/lazyTree.md.

NPM version Build Status Deps Up to Date

Features

  • The minified file is only 3.8KB (Bluebird / 73KB, ES6-promise / 18KB)
  • 100% compliant with Promise/A+ specs
  • Better performance than the native Promise
  • Designed to work on IE5+ and other major browsers
  • Better possibly unhandled rejection and long stack trace than Bluebird
  • Well commented source code with every Promise/A+ spec

Quick Start

Node.js

npm install yaku

Then:

Promise = require 'yaku'

Browser

Use something like Browserify or Webpack, or download the yaku.js file from release page. It supports both AMD and CMD. Raw usage without AMD or CMD:

<script type="text/javascript" src ="yaku.js"></script>
<script>
    // Yaku will be assigned to `window.Yaku`.
    var Promise = Yaku;
</script>

Compare

These comparisons only reflect some limited truth, no one is better than all others on all aspects.

iojs v1.8.1
OS   darwin
Arch x64
CPU  Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
Name Unit Test 1ms async task sync task Helpers file size
Yaku 872/872 283ms 68ms ++ 3.8KB
Bluebird v2.9 872/872 272ms 164ms +++++++ 73KB
ES6-promise v2.1 872/872 459ms 110ms + 18KB
native iojs v1.8 872/872 826ms 605ms + 0KB
q v1.3 208/872 2710ms 2327ms +++ 24K
  • Helpers: extra methods that help with your promise programming, such as async flow control helpers, debug helpers. For more details: docs/debugHelperComparison.md.
  • 1ms async task: npm run no -- benchmark, the smaller the better.
  • sync task: npm run no -- benchmark --sync, the smaller the better.

FAQ

  • Better long stack trace support?

    Latest Node.js and browsers are already support it. If you enabled it, Yaku will take advantage of it without much overhead. Such as this library longjohn for Node.js, or this article for Chrome.

  • catch on old brwoser (IE7, IE8 etc)?

    In ECMA-262 spec, catch cannot be used as method name. If you use coffee-script, it will handle the catch automatically, else you have to alias the method name or use something like Promise.resolve()['catch'](function() {}) or Promise.resolve().then(null, function() {}).

  • Will Yaku implement done, finally, promisify, etc?

    No. All non-ES6 APIs are only implemented for debugging and testing, which means when you remove Yaku, everything should work well with ES6 native promise. If you need fancy and magic, go for Bluebird.

  • Why use CoffeeScript, not Javascript?

    If it is really a problemn for people to use it, I will take time to translate it to JS. Else, I'd like to keep the code as simple as CoffeeScript.

  • The name Yaku is weird?

    The name yaku comes from the word ç´„ćťź(yakusoku) which means promise.

API

  • This class follows the Promises/A+ and ES6 spec with some extra helpers.

    • param: executor { Function }

      Function object with two arguments resolve and reject. The first argument fulfills the promise, the second argument rejects it. We can call these functions, once our operation is completed.

    • example:

      Promise = require 'yaku'
      p = new Promise (resolve, reject) ->
      	setTimeout ->
      		if Math.random() > 0.5
      			resolve 'ok'
      		else
      			reject 'no'
  • Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.

    • param: onFulfilled { Function }

      Optional. Called when the Promise is resolved.

    • param: onRejected { Function }

      Optional. Called when the Promise is rejected.

    • return: { Yaku }

      It will return a new Yaku which will resolve or reject after

    • example:

      the current Promise.

      Promise = require 'yaku'
      p = Promise.resolve 10
      
      p.then (v) ->
      	console.log v
  • The catch() method returns a Promise and deals with rejected cases only. It behaves the same as calling Promise.prototype.then(undefined, onRejected).

    • param: onRejected { Function }

      A Function called when the Promise is rejected. This function has one argument, the rejection reason.

    • return: { Yaku }

      A Promise that deals with rejected cases only.

    • example:

      Promise = require 'yaku'
      p = Promise.reject 10
      
      p.catch (v) ->
      	console.log v
  • The Promise.resolve(value) method returns a Promise object that is resolved with the given value. If the value is a thenable (i.e. has a then method), the returned promise will "follow" that thenable, adopting its eventual state; otherwise the returned promise will be fulfilled with the value.

    • param: value { Any }

      Argument to be resolved by this Promise. Can also be a Promise or a thenable to resolve.

    • return: { Yaku }

    • example:

      Promise = require 'yaku'
      p = Promise.resolve 10
  • The Promise.reject(reason) method returns a Promise object that is rejected with the given reason.

    • param: reason { Any }

      Reason why this Promise rejected.

    • return: { Yaku }

    • example:

      Promise = require 'yaku'
      p = Promise.reject 10
  • The Promise.race(iterable) method returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise.

    • param: iterable { iterable }

      An iterable object, such as an Array.

    • return: { Yaku }

      The race function returns a Promise that is settled the same way as the first passed promise to settle. It resolves or rejects, whichever happens first.

    • example:

      Promise = require 'yaku'
      Promise.race [
      	123
      	Promise.resolve 0
      ]
      .then (value) ->
      	console.log value # => 123
  • The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved.

    The result is passed as an array of values from all the promises. If something passed in the iterable array is not a promise, it's converted to one by Promise.resolve. If any of the passed in promises rejects, the all Promise immediately rejects with the value of the promise that rejected, discarding all the other promises whether or not they have resolved.

    • param: iterable { iterable }

      An iterable object, such as an Array.

    • return: { Yaku }

    • example:

      Promise = require 'yaku'
      Promise.all [
      	123
      	Promise.resolve 0
      ]
      .then (values) ->
      	console.log values # => [123, 0]
  • Catch all possibly unhandled rejections. If you want to use specific format to display the error stack, overwrite it. If it is set, auto console.error unhandled rejection will be disabed.

    • param: reason { Any }

      The rejection reason.

    • example:

      Promise = require 'yaku'
      Promise.onUnhandledRejection = (reason) ->
      	console.error reason
      
      # The console will log an unhandled rejection error message.
      Promise.reject('my reason')
      
      # The below won't log the unhandled rejection error message.
      Promise.reject('v').catch ->
  • It is used to enable the long stack trace. Once it is enabled, it can't be reverted. While it is very helpful in development and testing environments, it is not recommended to use it in production. It will slow down your application and waste your memory.

    • example:

      Promise = require 'yaku'
      Promise.enableLongStackTrace()

Utils

To use it you have to require it separately: utils = require 'yaku/lib/utils'. If you want to use it in the browser, you have to use browserify or webpack.

  • An throttled version of Promise.all, it runs all the tasks under a concurrent limitation. To run tasks sequentially, use utils.flow.

    • param: limit { Int }

      The max task to run at a time. It's optional. Default is Infinity.

    • param: list { Array | Function }

      If the list is an array, it should be a list of functions or promises, and each function will return a promise. If the list is a function, it should be a iterator that returns a promise, when it returns utils.end, the iteration ends. Of course it can never end.

    • param: saveResults { Boolean }

      Whether to save each promise's result or not. Default is true.

    • param: progress { Function }

      If a task ends, the resolve value will be passed to this function.

    • return: { Promise }

    • example:

      kit = require 'nokit'
      utils = require 'yaku/lib/utils'
      
      urls = [
       'http://a.com'
       'http://b.com'
       'http://c.com'
       'http://d.com'
      ]
      tasks = [
       -> kit.request url[0]
       -> kit.request url[1]
       -> kit.request url[2]
       -> kit.request url[3]
      ]
      
      utils.async(tasks).then ->
       kit.log 'all done!'
      
      utils.async(2, tasks).then ->
       kit.log 'max concurrent limit is 2'
      
      utils.async 3, ->
       url = urls.pop()
       if url
           kit.request url
       else
           utils.end
      .then ->
       kit.log 'all done!'
  • If a function returns promise, convert it to node callback style function.

    • param: fn { Function }

    • param: self { Any }

      The this to bind to the fn.

    • return: { Function }

  • Create a jQuery.Deferred like object.

  • The end symbol.

  • Creates a function that is the composition of the provided functions. Besides, it can also accept async function that returns promise. See utils.async, if you need concurrent support.

    • param: fns { Function | Array }

      Functions that return promise or any value. And the array can also contains promises or values other than function. If there's only one argument and it's a function, it will be treated as an iterator, when it returns utils.end, the iteration ends.

    • return: { Function }

      (val) -> Promise A function that will return a promise.

    • example:

      It helps to decouple sequential pipeline code logic.

      kit = require 'nokit'
      utils = require 'yaku/lib/utils'
      
      createUrl = (name) ->
      	return "http://test.com/" + name
      
      curl = (url) ->
      	kit.request(url).then (body) ->
      		kit.log 'get'
      		body
      
      save = (str) ->
      	kit.outputFile('a.txt', str).then ->
      		kit.log 'saved'
      
      download = utils.flow createUrl, curl, save
      # same as "download = utils.flow [createUrl, curl, save]"
      
      download 'home'
    • example:

      Walk through first link of each page.

      kit = require 'nokit'
      utils = require 'yaku/lib/utils'
      
      list = []
      iter = (url) ->
      	return utils.end if not url
      
      	kit.request url
      	.then (body) ->
      		list.push body
      		m = body.match /href="(.+?)"/
      		m[0] if m
      
      walker = utils.flow iter
      walker 'test.com'
  • Check if an object is a promise-like object.

    • param: obj { Object }

    • return: { Boolean }

  • Convert a node callback style function to a function that returns promise when the last callback is not supplied.

    • param: fn { Function }

    • param: self { Any }

      The this to bind to the fn.

    • return: { Function }

    • example:

      foo = (val, cb) ->
      	setTimeout ->
      		cb null, val + 1
      
      bar = utils.promisify(foo)
      
      bar(0).then (val) ->
      	console.log val # output => 1
      
      # It also supports the callback style.
      bar 0, (err, val) ->
      	console.log val # output => 1
  • Create a promise that will wait for a while before resolution.

    • param: time { Integer }

      The unit is millisecond.

    • param: val { Any }

      What the value this promise will resolve.

    • return: { Promise }

  • Throw an error to break the program.

    • param: err { Any }

    • example:

      Promise.resolve().then ->
      	# This error won't be caught by promise.
      	utils.throw 'break the program!'

Unit Test

This project use promises-aplus-tests to test the compliance of Promise/A+ specification. There are about 900 test cases.

Use npm run no -- test to run the unit test.

Benchmark

Use npm run no -- benchmark to run the benchmark.

Contribute

Other than use gulp, all my projects use nokit to deal with automation. Run npm run no -- -h to print all the tasks that defined in the nofile.coffee. If you installed nokit globally, you can just run no -h without npm run and --.

About

An ES6 Promise/A+ implementation that doesn't hurt.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • CoffeeScript 97.5%
  • HTML 1.9%
  • JavaScript 0.6%