123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- var Promise = require('any-promise');
- var co = require('co');
- module.exports = compose;
- compose.Wrap = Wrap;
- function compose(middleware) {
- return function (next) {
- next = next || new Wrap(noop);
- var i = middleware.length;
- while (i--) next = new Wrap(middleware[i], this, next);
- return next
- }
- }
- /**
- * Wrap a function, then lazily call it,
- * always returning both a promise and a generator.
- *
- * @param {Function} fn
- * @param {Object} ctx
- * @param {Wrap} next
- */
- function Wrap(fn, ctx, next) {
- if (typeof fn !== 'function') throw TypeError('Not a function!');
- this._fn = fn;
- this._ctx = ctx;
- this._next = next;
- this._called = false;
- this._value = undefined;
- this._promise = undefined;
- this._generator = undefined;
- }
- /**
- * Lazily call the function.
- * Note that if it's not an async or generator function,
- *
- * @returns {Mixed}
- */
- Wrap.prototype._getValue = function () {
- if (!this._called) {
- this._called = true;
- try {
- this._value = this._fn.call(this._ctx, this._next);
- } catch (e) {
- this._value = Promise.reject(e);
- }
- }
- return this._value
- };
- /**
- * Lazily create a promise from the return value.
- *
- * @returns {Promise}
- */
- Wrap.prototype._getPromise = function () {
- if (this._promise === undefined) {
- var value = this._getValue();
- this._promise = isGenerator(value)
- ? co.call(this._ctx, value)
- : Promise.resolve(value);
- }
- return this._promise
- }
- /**
- * Lazily create a generator from the return value.
- *
- * @returns {Iterator}
- */
- Wrap.prototype._getGenerator = function () {
- if (this._generator === undefined) {
- var value = this._getValue();
- this._generator = isGenerator(value)
- ? value
- : promiseToGenerator.call(this._ctx, value);
- }
- return this._generator
- }
- /**
- * In later version of v8,
- * `yield*` works on the `[@@iterator]` method.
- *
- * @returns {Iterator}
- */
- if (typeof Symbol !== 'undefined') {
- Wrap.prototype[Symbol.iterator] = function () {
- return this._getGenerator();
- }
- }
- /**
- * This creates a generator from a promise or a value.
- *
- * TODO: try to avoid using a generator function for this.
- *
- * @returns {Iterator}
- */
- var loggedDeprecationMessage = false;
- function* promiseToGenerator(promise) {
- if (!loggedDeprecationMessage) {
- console.error('A promise was converted into a generator, which is an anti-pattern. Please avoid using `yield* next`!')
- loggedDeprecationMessage = true;
- }
- return yield Promise.resolve(promise);
- }
- /**
- * Proxy generator and promise methods.
- */
- Wrap.prototype.then = function (resolve, reject) {
- return this._getPromise().then(resolve, reject);
- }
- Wrap.prototype.catch = function (reject) {
- return this._getPromise().catch(reject);
- }
- Wrap.prototype.next = function (val) {
- return this._getGenerator().next(val);
- }
- Wrap.prototype.throw = function (err) {
- return this._getGenerator().throw(err);
- }
- /**
- * Check if `obj` is a generator.
- *
- * @param {Mixed} obj
- * @return {Boolean}
- */
- function isGenerator(obj) {
- return obj
- && typeof obj.next === 'function'
- && typeof obj.throw === 'function';
- }
- function noop() {}
|