Giter VIP home page Giter VIP logo

yaku's Introduction

Promises/A+ logo

Overview

Yaku is full compatible with ES6's native Promise, but much faster, and more error friendly. If you want to learn how Promise works, read the minimum implementation yaku.aplus. Without comments, it is only 80 lines of code (gzipped size is 0.5KB). It only implements the constructor and then.

Yaku passed all the tests of promises-aplus-tests, promises-es6-tests, and even the core-js tests.

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

NPM version Build Status Coverage Status

Features

  • One of the best for mobile, gzipped file is only 2.0KB
  • Supports "uncaught rejection" and "long stack trace", Comparison
  • Works on IE5+ and other major browsers
  • 100% statement and branch test coverage
  • Better CPU and memory performance than the native Promise
  • Well commented source code with every Promises/A+ spec
  • Highly modularized extra helpers, no pollution to its pure ES6 implementation
  • Supports ES7 finally
  • Composable observable helper

Quick Start

Node.js

npm install yaku

Then:

var Promise = require('yaku');

Or if you don't want any extra debug helper, ES6 only version is here:

var Promise = require('yaku/lib/yaku.core');

Or if you only want aplus support:

var Promise = require('yaku/lib/yaku.aplus');

Browser

Raw usage:

<script type="text/javascript" src="https://raw.githubusercontent.com/ysmood/yaku/master/src/yaku.js"></script>
<script>
    // Yaku will be assigned to `window.Yaku`.
    var Promise = Yaku;
</script>

Change Log

docs/changelog.md

Compare to Other Promise Libs

These comparisons only reflect some limited truth, no one is better than all others on all aspects. There are tons of Promises/A+ implementations, you can see them here. Only some of the famous ones were tested.

You can reproduce it on your own machine with npm run no -- benchmark.

Date: Wed Aug 15 2018 23:06:02 GMT+0900 (Japan Standard Time)
Node v10.8.0
OS   darwin
Arch x64
CPU  Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
name unit tests coverage 1ms async task optional helpers helpers gzip
yaku@0.19.1 100% 100% 274ms / 112MB 35 1.9KB
yaku.core@0.19.1 100% 100% 292ms / 110MB 29 1.6KB
yaku.aplus@0.19.1 x (91 failed) 100% 100% 328ms / 126MB 7 0.5KB
bluebird@3.5.1 x (33 failed) 99% 96% 183ms / 88MB partial 103 16.1KB
es6-promise@4.2.4 x (48 failed) ? ? 510ms / 106MB x 13 2.4KB
pinkie@2.0.4 x (45 failed) ? ? 284ms / 138MB v 10 1.2KB
native@10.8.0 ? ? 133ms / 86MB x 11 0KB
core-js@2.5.7 x (5 failed) ? ? 239ms / 99MB x 13 4.8KB
es6-shim@0.35.3 ? ? 161ms / 87MB x 11 15.6KB
q@1.5.1 x (21 failed) ? ? 896ms / 326MB x 74 4.7KB
my-promise@1.1.0 x (10 failed) ? ? 583ms / 224MB x 10 4KB
  • unit test: promises-aplus-tests, promises-es6-tests, and even the core-js tests.

  • coverage: statement coverage and branch coverage.

  • 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 (total time / memory rss).

  • promises-es6-tests: If you want to test bluebird against promises-es6-tests, run npm run no -- test-es6 --shim bluebird.

  • optional helpers: Whether the helpers can be imported separately or not, which means you can load the lib without helpers. Such as the bluebird-core, it will inevitably load some nonstandard helpers: spread, promisify, etc.

FAQ

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

    In ECMA-262 spec, catch cannot be used as method name. You have to alias the method name or use something like Promise.resolve()['catch'](function() {}) or Promise.resolve().then(null, function() {}).

  • When using with Babel and Regenerator, the unhandled rejection doesn't work.

    Because Regenerator use global Promise directly and don't have an api to set the Promise lib. You have to import Yaku globally to make it use Yaku: require("yaku/lib/global");.

Unhandled Rejection

Yaku will report any unhandled rejection via console.error by default, in case you forget to write catch. You can catch them manually:

  • Browser: window.onunhandledrejection = ({ promise, reason }) => { /* Your Code */ };
  • Node: process.on("unhandledRejection", (reason, promise) => { /* Your Code */ });

For more spec read Unhandled Rejection Tracking Browser Events.

API


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

    • param: executor { Function }

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

  • 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.

      var Promise = require('yaku');
      var 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:

      var Promise = require('yaku');
      var p = Promise.reject(new Error("ERR"));
      
      p['catch']((v) => {
          console.log(v);
      });
  • Register a callback to be invoked when a promise is settled (either fulfilled or rejected). Similar with the try-catch-finally, it's often used for cleanup.

    • param: onFinally { Function }

      A Function called when the Promise is settled. It will not receive any argument.

    • return: { Yaku }

      A Promise that will reject if onFinally throws an error or returns a rejected promise. Else it will resolve previous promise's final state (either fulfilled or rejected).

    • example:

      var Promise = require('yaku');
      var p = Math.random() > 0.5 ? Promise.resolve() : Promise.reject();
      p.finally(() => {
          console.log('finally');
      });
  • 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:

      var Promise = require('yaku');
      var 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:

      var Promise = require('yaku');
      var p = Promise.reject(new Error("ERR"));
  • 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:

      var 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:

      var Promise = require('yaku');
      Promise.all([
          123,
          Promise.resolve(0)
      ])
      .then((values) => {
          console.log(values); // => [123, 0]
      });
    • example:

      Use with iterable.

      var Promise = require('yaku');
      Promise.all((function * () {
          yield 10;
          yield new Promise(function (r) { setTimeout(r, 1000, "OK") });
      })())
      .then((values) => {
          console.log(values); // => [123, 0]
      });
  • The Promise.allSettled(iterable) method returns a promise that resolves after all of the given promises have either resolved or rejected, with an array of objects that each describes the outcome of each promise.

    • param: iterable { iterable }

      An iterable object, such as an Array.

    • return: { Yaku }

      A promise resolves a list of objects. For each object, a status string is present. If the status is fulfilled, then a value is present. If the status is rejected, then a reason is present. The value (or reason) reflects what value each promise was fulfilled (or rejected) with.

    • example:

      var Promise = require('yaku');
      Promise.allSettled([
          Promise.resolve(3),
          new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'))
      ])
      .then((values) => {
          console.log(values); // => [{status: "fulfilled", value: 3}, {status: "rejected", reason: "foo"}]
      });
  • The ES6 Symbol object that Yaku should use, by default it will use the global one.

    • type: { Object }

    • example:

      var core = require("core-js/library");
      var Promise = require("yaku");
      Promise.Symbol = core.Symbol;
  • Use this api to custom the species behavior. https://tc39.github.io/ecma262/#sec-speciesconstructor

    • param: O { Any }

      The current this object.

    • param: defaultConstructor { Function }

  • 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 disabled.

    • param: reason { Any }

      The rejection reason.

    • param: p { Yaku }

      The promise that was rejected.

    • example:

      var Promise = require('yaku');
      Promise.unhandledRejection = (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"](() => {});
  • Emitted whenever a Promise was rejected and an error handler was attached to it (for example with ["catch"]()) later than after an event loop turn.

    • param: reason { Any }

      The rejection reason.

    • param: p { Yaku }

      The promise that was rejected.

  • 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 application and eat up memory. It will add an extra property longStack to the Error object.

    • example:

      var Promise = require('yaku');
      Promise.enableLongStackTrace();
      Promise.reject(new Error("err"))["catch"]((err) => {
          console.log(err.longStack);
      });
  • Only Node has process.nextTick function. For browser there are so many ways to polyfill it. Yaku won't do it for you, instead you can choose what you prefer. For example, this project next-tick. By default, Yaku will use process.nextTick on Node, setTimeout on browser.

    • type: { Function }

    • example:

      var Promise = require('yaku');
      Promise.nextTick = require('next-tick');
    • example:

      You can even use sync resolution if you really know what you are doing.

      var Promise = require('yaku');
      Promise.nextTick = fn => fn();

Utils

It's a bundle of all the following functions. You can require them all with var yutils = require("yaku/lib/utils"), or require them separately like require("yaku/lib/flow"). If you want to use it in the browser, you have to use browserify or webpack. You can even use another Promise lib, such as:

require("yaku/lib/_").Promise = require("bluebird");
var source = require("yaku/lib/source");

// now "source" use bluebird instead of yaku.
  • A function that helps run functions under a concurrent limitation. To run functions sequentially, use yaku/lib/flow.

    • param: limit { Int }

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

    • param: list { Iterable }

      Any iterable object. It should be a lazy iteralbe object, don't pass in a normal Array with promises.

    • return: { Promise }

    • example:

      var kit = require('nokit');
      var all = require('yaku/lib/all');
      
      var urls = [
          'http://a.com',
          'http://b.com',
          'http://c.com',
          'http://d.com'
      ];
      var tasks = function * () {
          var i = 0;
          yield kit.request(url[i++]);
          yield kit.request(url[i++]);
          yield kit.request(url[i++]);
          yield kit.request(url[i++]);
      }();
      
      all(tasks).then(() => kit.log('all done!'));
      
      all(2, tasks).then(() => kit.log('max concurrent limit is 2'));
      
      all(3, { next: () => {
          var url = urls.pop();
          return {
               done: !url,
               value: url && kit.request(url)
          };
      } })
      .then(() => kit.log('all done!'));
  • Similar with the Promise.race, but only rejects when every entry rejects.

    • param: iterable { iterable }

      An iterable object, such as an Array.

    • return: { Yaku }

    • example:

      var any = require('yaku/lib/any');
      any([
          123,
          Promise.resolve(0),
          Promise.reject(new Error("ERR"))
      ])
      .then((value) => {
          console.log(value); // => 123
      });
  • Generator based async/await wrapper.

    • param: gen { Generator }

      A generator function

    • return: { Yaku }

    • example:

      var async = require('yaku/lib/async');
      var sleep = require('yaku/lib/sleep');
      
      var fn = async(function * () {
          return yield sleep(1000, 'ok');
      });
      
      fn().then(function (v) {
          console.log(v);
      });
  • 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 }

  • deprecate Create a jQuery.Deferred like object. It will cause some buggy problems, please don't use it.

  • Creates a function that is the composition of the provided functions. See yaku/lib/async, if you need concurrent support.

    • param: list { Iterable }

      Any iterable object. It should be a lazy iteralbe object, don't pass in a normal Array with promises.

    • return: { Function }

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

    • example:

      It helps to decouple sequential pipeline code logic.

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

      Walk through first link of each page.

      var kit = require('nokit');
      var flow = require('yaku/lib/flow');
      
      var list = [];
      function iter (url) {
          return {
              done: !url,
              value: url && kit.request(url).then((body) => {
                  list.push(body);
                  var m = body.match(/href="(.+?)"/);
                  if (m) return m[0];
              });
          };
      }
      
      var walker = flow(iter);
      walker('test.com');
  • Enable a helper to catch specific error type. It will be directly attach to the prototype of the promise.

    • param: type { class }

    • param: onRejected { Function }

    • return: { Promise }

      var Promise = require('yaku');
      require('yaku/lib/guard');
      
      class AnError extends Error {
      }
      
      Promise.reject(new AnError('hey'))
      .guard(AnError, (err) => {
           // only log AnError type
           console.log(err);
      })
      .then(() => {
           console.log('done');
      })
      .guard(Error, (err) => {
           // log all error type
           console.log(err)
      });
  • Just like Promise.all, but you pass in a key-value dict, after every value is resolved, it will resolve a k-v dict.

    • param: dict { Object }

    • return: { Promise }

      var hash = require('yaku/lib/hash');
      var sleep = require('yaku/lib/sleep');
      
      hash({
          a: sleep(100, 'a'),
          b: sleep(200, 'b')
      }).then((dict) => { // will resolve after 200ms, not 300ms
          console.log(dict.a, dict.b)
      })
  • if-else helper

    • param: cond { Promise }

    • param: trueFn { Function }

    • param: falseFn { Function }

    • return: { Promise }

    • example:

      var Promise = require('yaku');
      var yutils = require('yaku/lib/utils');
      
      yutils.if(Promise.resolve(false), () => {
          // true
      }, () => {
          // false
      })
  • deprecate Check if an object is a promise-like object. Don't use it to coercive a value to Promise, instead use Promise.resolve.

    • param: obj { Any }

    • return: { Boolean }

  • Create a promise that never ends.

    • return: { Promise }

      A promise that will end the current pipeline.

  • 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:

      var promisify = require('yaku/lib/promisify');
      function foo (val, cb) {
          setTimeout(() => {
              cb(null, val + 1);
          });
      }
      
      var bar = 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 }

    • example:

      var sleep = require('yaku/lib/sleep');
      sleep(1000).then(() => console.log('after one second'));
  • Read the Observable section.

    • type: { Function }
  • Retry a function until it resolves before a mount of times, or reject with all the error states.

    • version_added:

      v0.7.10

    • param: countdown { Number | Function }

      How many times to retry before rejection.

    • param: span { Number }

      Optional. How long to wait before each retry in millisecond. When it's a function (errs) => Boolean | Promise.resolve(Boolean), you can use it to create complex countdown logic, it can even return a promise to create async countdown logic.

    • param: fn { Function }

      The function can return a promise or not.

    • param: this { Any }

      Optional. The context to call the function.

    • return: { Function }

      The wrapped function. The function will reject an array of reasons that throwed by each try.

    • example:

      Retry 3 times before rejection, wait 1 second before each retry.

      var retry = require('yaku/lib/retry');
      var { request } = require('nokit');
      
      retry(3, 1000, request)('http://test.com').then(
         (body) => console.log(body),
         (errs) => console.error(errs)
      );
    • example:

      Here a more complex retry usage, it shows an random exponential backoff algorithm to wait and retry again, which means the 10th attempt may take 10 minutes to happen.

      var retry = require('yaku/lib/retry');
      var sleep = require('yaku/lib/sleep');
      var { request } = require('nokit');
      
      function countdown (retries) {
         var attempt = 0;
         return async () => {
              var r = Math.random() * Math.pow(2, attempt) * 1000;
              var t = Math.min(r, 1000 * 60 * 10);
              await sleep(t);
              return attempt++ < retries;
         };
      }
      
      retry(countdown(10), request)('http://test.com').then(
         (body) => console.log(body),
         (errs) => console.error(errs)
      );
  • Throw an error to break the program.

    • param: err { Any }

    • example:

      var ythrow = require('yaku/lib/throw');
      Promise.resolve().then(() => {
          // This error won't be caught by promise.
          ythrow('break the program!');
      });
  • Create a promise that will reject after a while if the passed in promise doesn't settle first.

    • param: promise { Promise }

      The passed promise to wait.

    • param: time { Integer }

      The unit is millisecond.

    • param: reason { Any }

      After time out, it will be the reject reason.

    • return: { Promise }

    • example:

      var sleep = require('yaku/lib/sleep');
      var timeout = require('yaku/lib/timeout');
      timeout(sleep(500), 100)["catch"]((err) => {
          console.error(err);
      });

Observable

  • Create a composable observable object. Promise can't resolve multiple times, this class makes it possible, so that you can easily map, filter and even back pressure events in a promise way. For live example: Double Click Demo.

    • version_added:

      v0.7.2

    • param: executor { Function }

      (next) -> It's optional.

    • return: { Observable }

    • example:

      var Observable = require("yaku/lib/Observable");
      var linear = new Observable();
      
      var x = 0;
      setInterval(linear.next, 1000, x++);
      
      // Wait for 2 sec then emit the next value.
      var quad = linear.subscribe(async x => {
          await sleep(2000);
          return x * x;
      });
      
      var another = linear.subscribe(x => -x);
      
      quad.subscribe(
          value => { console.log(value); },
          reason => { console.error(reason); }
      );
      
      // Emit error
      linear.error(new Error("reason"));
      
      // Unsubscribe an observable.
      quad.unsubscribe();
      
      // Unsubscribe all subscribers.
      linear.subscribers = [];
    • example:

      Use it with DOM.

      var filter = fn => v => fn(v) ? v : new Promise(() => {});
      
      var keyup = new Observable((next) => {
          document.querySelector('input').onkeyup = next;
      });
      
      var keyupText = keyup.subscribe(e => e.target.value);
      
      // Now we only get the input when the text length is greater than 3.
      var keyupTextGT3 = keyupText.subscribe(filter(text => text.length > 3));
      
      keyupTextGT3.subscribe(v => console.log(v));
  • Emit a value.

    • param: value { Any }

      so that the event will go to onError callback.

  • Emit an error.

    • param: value { Any }
  • The publisher observable of this.

    • type: { Observable }
  • All the subscribers subscribed this observable.

    • type: { Array }
  • It will create a new Observable, like promise.

    • param: onNext { Function }

    • param: onError { Function }

    • return: { Observable }

  • Unsubscribe this.

  • Merge multiple observables into one.

    • version_added:

      0.9.6

    • param: iterable { Iterable }

    • return: { Observable }

    • example:

      var Observable = require("yaku/lib/Observable");
      var sleep = require("yaku/lib/sleep");
      
      var src = new Observable(next => setInterval(next, 1000, 0));
      
      var a = src.subscribe(v => v + 1; });
      var b = src.subscribe((v) => sleep(10, v + 2));
      
      var out = Observable.merge([a, b]);
      
      out.subscribe((v) => {
          console.log(v);
      })

Unit Test

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

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

Test other libs

basic test

To test bluebird: npm run no -- test-basic --shim bluebird

The bluebird can be replaced with other lib, see the test/getPromise.js for which libs are supported.

aplus test

To test bluebird: npm run no -- test-aplus --shim bluebird

The bluebird can be replaced with other lib, see the test/getPromise.js for which libs are supported.

es6 test

To test bluebird: npm run no -- test-es6 --shim bluebird

The bluebird can be replaced with other lib, see the test/getPromise.js for which libs are supported.

Benchmark

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

async/await generator wrapper

Node v5.6.0
OS   darwin
Arch x64
CPU  Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz

yaku: 117ms
co: 283ms
bluebird: 643ms

Contribution

Make sure you have npm and npm install at the root of the project first.

Other than use gulp, all my projects use nokit to deal with automation. Run npm run no -- -h to print all the tasks that you can use.

Update readme.md

Please don't alter the readme.md directly, it is compiled from the docs/readme.jst.md. Edit the docs/readme.jst.md and execute npm run no to rebuild the project.

yaku's People

Contributors

2hu12 avatar andrewwakeling avatar boriskor avatar frosas avatar gtanczyk avatar jakechampion avatar maxnordlund avatar squarepants avatar ysmood 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

yaku's Issues

long stack traces

Are there any benchmarks which say what the trade-off is with using long stack traces?

Thanks

Bind to Angular 1.x digest?

Bluebird provides the Promise.setScheduler method to bind with the angular digest cycle.

// This will synchronize bluebird promise queue flushing with angulars queue flushing
// Angular is also now responsible for choosing the actual scheduler
Promise.setScheduler(function(fn) {
    $rootScope.$evalAsync(fn);
});

Can the same thing be achieved with Yaku?

`then` memory leak

If we call a promise's then method multiple times, it may potentially cause memory leak:

var p = Promise.resolve();

p.then(function () {});
p.then(function () {});
p.then(function () {});
p.then(function () {});
p.then(function () {});

process.on('exit', () => {
    p = null;
});

If we don't release variable p, its children promises will stay in memory even after p and all the children are settled. What we should do with this situation is not specified in the ES6 yet.

I think we should cut off the link between the parent and child immediately after the child is settled.

Convert coffee to js

It will decrease the code base a little, and make it more friendly for beginners.

Promisify ReadStream

Such as

var utils = require('yaku/lib/utils');
var f = fs.createReadStream('test');

var p = utils.promisify(f);

p.then(function (data) {
    console.log(data);
})

Pass data (callback) to unhandledRejection handler

I have a case, when I need pass callback to unhandledRejection handler and can't use catch in default maner, when I try define property for the promise, I notice that property do not passed in promise at onUnhandledRejection, how I can implement this behaviour?

Example:

const onUnhandledRejection = (reason, promise) => {
  // promise.handleRejection always undefined
  if (promise && typeof promise.handleRejection === 'function') {
    promise.handleRejection(reason);
  } else {
    console.error(reason);
  }
};

window.onunhandledrejection = ({ reason, promise }) => {
    onUnhandledRejection(reason, promise);
};

const handleRejection = (promise) => {
  Object.defineProperty(promise, 'handleRejection', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: (reason) => {
      if (reason && reason instanceof SkipError) return;
      console.error('MIDDLEWARE ERROR:', reason, reason.stack);
      // ...customize global rejection handler for this promise
    }
  });

  return promise;
};

handleRejection(promise(client)).then(
  (result) => {
    // ...
  },
  (error) => {
    throw SkipError;
  }
);

instance constructor is not Promise

I'm using promise.prototype.finally to add the finally functionaity to Promises, but that depends on the promise's constructor to be Promise. Currently the constructor is Object.

> (new Promise(function() {})).constructor
Object() { [native code] }

difference between return Promise.reject(x) and throw x

I can track down further if its not obvious but I've been trying to work out why I get unhandled rejections for promises that are handled. I've not been able to make a reduced test case, but the behaviour seems to occur when I have something like

promise.catch(() => {
    // maybe do something different
    return Promise.reject({});
}).catch(() => {
    // absorb rejection so unhandledrejection should not be called
});

and it appears changing the return to throw make it work and not give unhandled rejections (which is the expected behaviour).

incorrect behavior with unhandledrejection events

Per https://github.com/ysmood/yaku/blob/master/docs/debugHelperComparison.md#better-unhandled-error, Yaku will not fire an unhandledrejection event in this case:

p0 = Yaku.resolve().then(function () {
    abc()
})
p1 = p0.then(function () {})
p2 = p0.then(function () {})
p3 = p0.catch(function () {})

From my understanding, this is incorrect behavior. p1 and p2 are new promises that have unhandled rejections.

I think the following should happen:

window.onunhandledrejection = ({promise}) => { 
  console.log('window.onunhandledrejection: p' + promises.indexOf(promise));
}
p0 = Yaku.resolve().then(function () {
    abc()
})
p1 = p0.then(function () {})
p2 = p0.then(function () {})
p3 = p0.catch(function () {})
promises = [p0, p1, p2, p3]

// should log 'unhandledrejection: p1'
// should log 'unhandledrejection: p2'

I believe Chrome actually implements the spec correctly.

Mock Mode

Hi,

We use Yaku for production and had a need for a mock mode to allow synchronous tests.

I hacked it in:
https://github.com/lukeapage/yaku-mock

If I cleaned it up, abstracted and made a PR back to you, would you be interested? or should I keep it as a fork?

Yaku doesn't work if div with id="process" exists

This is the weirdest glitch I've found in my entire existence.

If you add a <div id="process"> to the DOM, it breaks.

The problem is that Yaku checks for window.process, which seems to be defined in some browsers as $("#process"). Yaku then tries to use process.nextTick, which of course doesn't exist.

This doesn't happen in all browsers; not sure why.

Can't register global unhandled rejection

i try to add a handler for all unhandled rejection:
window.onunhandledrejection = ({ reason, promise }) => { console.log('help');};
Promise.reject(new Error("ERR"));

The only output i can see is the default 'Potentially unhandled rejection [1] Error: ERR'.
Is it a bug or am i using it wrong?

IE compatibility

You use Error#stack quite a bit in the code which was only supported by IE10+

Docs about utilities, and utilities

Hey, the docs say that the library does not offer things like .done, but it appears it just exposes a (differently named) .end instead.

I warmly recommend the following:

  • Remove .defer, deferreds are super problematic anyway, they were decided against in the official API for the Zalgo problem they cause.
  • Remove .end, you have unhandled rejection detection already - that's better anyway.
  • Don't expose .isPromise, that encourages (in my experience) users to rely on it instead of Promise.resolveing results.
  • Move source to another unrelated repo, it is practically a mini observable. It's worth documenting separately.
  • Don't show examples in the docs that throw non-Errors as that breaks stack traces, as a library that implements long stack traces I assume you care about that.

Bug: Stealing a resolver and using it to trigger reentrancy

var Promise = require("yaku");
var assert = require('assert');

var stolenResolver;
var StealingPromiseConstructor = function StealingPromiseConstructor(resolver) {
	stolenResolver = resolver;
	resolver(
		function () {},
		function () {}
	);
};
var iterable = {};
var atAtIterator = '@@iterator'; // on firefox, at least.
iterable[atAtIterator] = function () {
	stolenResolver(null, null);
	throw new Error(0);
};
assert.doesNotThrow(function () {
	Promise.all.call(StealingPromiseConstructor, iterable);
});

It throws this:

AssertionError: Got unwanted exception.
Actual message: "self.reject is not a function"

This test was taken from es6-shim -- https://github.com/paulmillr/es6-shim/blob/4a815033b35a051632e035e1ec95aa3b1b43bfdd/test/promise/simple.js#L34-L51

global.Promise should be non-enumerable and should have correct name property

Expected:

> Object.getOwnPropertyDescriptor(global,'Promise');
{ value: [Function: Promise],
  writable: true,
  enumerable: false,
  configurable: true }

> global.Promise.name
'Promise'

Actual:

> Object.getOwnPropertyDescriptor(global,'Promise');
{ value:
   { [Function]
     default: [Circular],
     resolve: [Function],
     reject: [Function],
     race: [Function],
     all: [Function],
     Symbol: [Function: Symbol],
     speciesConstructor: [Function],
     unhandledRejection: [Function],
     rejectionHandled: [Function: en],
     enableLongStackTrace: [Function],
     nextTick: [Function: nextTick],
     _s: 1 },
  writable: true,
  enumerable: true,
  configurable: true }

> global.Promise.name
''

handled rejection can not be recognized

When I use catch or the second argument of then to handle a rejection, Unhandled Rejection is always shown.

yaku = require 'yaku'
new yaku (resolve, reject) ->
    reject 1
.catch ->

the result is

Unhandled Rejection: 1

I think this bug is introduced in v0.2.0

promisify

Hey, if you want node usage, it would be immensely useful to support promisify natively. Without the library supporting it itself, it is impossible to make promisify comparably fast.

See bluebird's https://github.com/petkaantonov/bluebird/blob/master/src/promisify.js for inspiration :)

I saw you already have a promisify.js file, but it is slow (uses a closure and .slice).

A promisifyAll would also be immensely useful for server side use.

Incorrect promise when calling `resolve` twice

Here is an edge case bug that we found in our production code. In the new Promise() constructor, if the resolve callback is called twice with another promise, then the second call to resolve overwrites the first call. A simple test case:

//comment out this line to test native version
const Promise = require('yaku');

const firstValue = Promise.resolve('correct');
const testPromise = new Promise(resolve => {
  resolve(firstValue); //first resolve another promise
  resolve('wrong'); //then resolve a value
});
//this line should log `correct`, but logs `wrong`
testPromise.then(x => console.log(x));

This only seems to happen when firstValue is a promise, when it's just the string 'correct' the code works the same as the native Promise implementation.

As I said at the beginning, this is an edge case bug, as calling resolve multiple times is not good, but we had an issue with this in production (we use polyfill.io for IE11 support), so I thought I'd at least let you know of the issue.

Something strange (Slow resolve on pageload)

Hello hello

First things first, i like your library ❤️ 🙉 , it provides huge benefits compared to other promraries (promise-libraries 😄)! Mainly its size is a really killer feature! But the longStackTraces are a good thing too.

But now I found a very strange problem: As soon as I use Yaku with rollup or brunch (yes I tried both), I notice a very long wait time from resolve to then. It is loading and loading, while a bluebird promise which was started in the same time as yaku, already has resolved.

dec 25 2016 10-14 pm - edited

(When I try yaku promises after the page is fully loaded, it works without problems, this slow resolve only happens on pageload)

Do you have an idea?

This is the code:

Yaku.resolve().then(function() {
  console.log('yaku1')
})

Yaku.resolve().then(function() {
  console.log('yaku2')
})

bluebird.resolve().then(function() {
  console.log('bluebird')
})

One thing I noticed too is, that when I console.log(Yaku) this log takes the same time to complete as the Yaku resolves! It is very strange: first the log just outputs function, and then it magical changes the function to:

function Promise(executor) {
        var self = this,
            err;

        // "this._s" is the internal state of: pending, resolved or rejected
        // "this._v" is the internal value

       

This is the rollup config I used:

var flow = require('rollup-plugin-flow');
import babel from 'rollup-plugin-babel';
var serve = require('rollup-plugin-serve')
var path = require('path')
import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';

export default {
  entry: 'app/main.js',
  dest: 'app/assets/bundle.js',
  plugins: [ flow(), nodeResolve({
        jsnext: true, main: true
      }), commonjs(), serve({
    contentBase: path.join(__dirname, 'app', 'assets')
      }) ],
  format: 'umd'
};

Callbackify won't callback if the callback is the only argument

This prints done as expected:

callbackify(function() {
  return Y.resolve('a');
})(1, function() {
  return console.log('done');
});

while this does not:

callbackify(function() {
  return Y.resolve('a');
})(function() {
  return console.log('done');
});

The only difference is the first example passes an extra (unused) argument to the function returned by callbackify.

Does not work on UC Browser

The following code is not working in UC Browser, because this is undefined inside an IIFE in UC Browser.

function(module, exports, __webpack_require__) {

	/* WEBPACK VAR INJECTION */(function(global) {var Yaku = __webpack_require__(/*! ./yaku */ 80);

	try {
	    global.Promise = Yaku;
	    window.Promise = Yaku;
	} catch (err) {
	    null;
	}
	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))

/***/ }

Documentation for `async`

I think the documentation for .async is very misleading since it says:

An throttled version of Promise.all

A lot of people, in my experience answering questions in StackOverflow, being involved in the spec process and helping maintain a library is that a lot of people mix up the fact a promise is a proxy for the value of an already started operation.

A throttled Promise.all is impossible of course since all the inputs to .all are already promises which means the operations themselves have already started.

It would be useful to update the documentation to reflect that, also it would be nice if the docs were moved to an API.md file or something similar.

utils aync strategy problem

This issue is inspired by the exercise, as described in the test case of this exercise, when the throttled-promise handler get all it's tasks done, the saveResults should keep their order as they are the result of each one of list

async: (limit, list, saveResults, progress) ->
  ...

Also, I modify one existed test case of yours, to show the difference.

test 'async array', [1, 2, 3], ->
  list = [
    -> utils.sleep 90, 1
    -> utils.sleep 10, 2
    -> utils.sleep 10, 3
  ]

  utils.async 2, list

The old version will get [2, 3, 1] instead of [1, 2, 3].

And there is a pull request. I hope myself get this right.

"hash" util/helper request

Could we add a hash helper like the RSVP, I think it's very convenient and useful? You think that is OK with you? Maybe I could implement it myself and make a PR.

A better default onUnhandledRejection handler

console.trace is not a good enough way to show stack trace. Also, uglified code make it pretty difficult to debug.

Unhandled rejection Error: [TypeError: Cannot read property 'value' of undefined]
Trace
at Function.t.onUnhandledRejection (/usr/local/lib/node_modules/nokit/node_modules/nofs/node_modules/yaku/dist/yaku.js:6:822)
at /usr/local/lib/node_modules/nokit/node_modules/nofs/node_modules/yaku/dist/yaku.js:6:2168
at r (/usr/local/lib/node_modules/nokit/node_modules/nofs/node_modules/yaku/dist/yaku.js:6:1171)
at process._tickCallback (node.js:442:13)

Finally swallows rejections

If I call finally on a rejected promise it unexpectedly becomes fulfilled.

const Promise = require(process.argv[2]);

const p = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('simulated error')));
});

p.finally(() => {
  console.log('Cleanup');
})
.then(result => {
  console.log('Unexpected success', result);
})
.catch(err => {
  console.error('Expected failure', err);
});

With yaku:

$ node index.js yaku
Cleanup
Unexpected success Error: simulated error

With bluebird:

$ node index.js bluebird
Cleanup
Expected failure Error: simulated error

Your finally test doesn't catch this because although you expect 'error' here: https://github.com/ysmood/yaku/blob/master/test/finally.js#L28

You do not confirm that its a rejection here: https://github.com/ysmood/yaku/blob/master/test/testSuit.js#L14

The cause is that you do not rethrow or wrap value with Promise.reject here:

return value;

Support global unhandled rejection detection events

Basically https://gist.github.com/benjamingr/0237932cee84712951a2

Most promise libraries and native promises in NodeJS support the global rejection events, these events in NodeJS allow code using multiple promise libraries and native promises in the same project to handle rejecitons globally and uniformally throughout the application and to provide a useful hook.

It also makes switching libraries simpler.

It would be great if Yaku could support it, I don't mind writing the PR but I think that you already know how to :)

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.