UNPKG

art-standard-lib

Version:

The Standard Library for JavaScript that aught to be.

463 lines (378 loc) 15 kB
// Generated by CoffeeScript 1.12.7 (function() { var BlueBirdPromise, ErrorWithInfo, Promise, deepEach, deepMap, defineModule, getEnv, isFunction, isPlainObject, isPromise, namespace, promiseDebug, ref, slice = [].slice; Promise = BlueBirdPromise = require("bluebird"); ref = require('./TypesExtended'), deepMap = ref.deepMap, deepEach = ref.deepEach, isFunction = ref.isFunction, isPlainObject = ref.isPlainObject; defineModule = require('./CommonJs').defineModule; getEnv = require('./Environment').getEnv; namespace = require('./namespace'); if (promiseDebug = getEnv().artPromiseDebug) { console.log("Art.StandardLib.Promise: BlueBirdPromise debug ENABLED"); } BlueBirdPromise.config({ warnings: promiseDebug, longStackTraces: promiseDebug, cancellation: promiseDebug, monitoring: promiseDebug }); isPromise = require('./Core/Types').isPromise; ErrorWithInfo = require("./ErrorWithInfo"); /* ArtPromise extends ES6 Promises in the following ways: - constructing a promise with no parameters is allowed - promise.resolve and promise.reject are supported as alternative ways to resolve or reject a promise If native promises are supported, they are used, otherwise a polyfill is used. TODO: ES6 says Promises are designed to be extensible: http://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects If I properly extend Promise, will my new methods be available on all promise objects... ??? At least all promises chained off of one created using my Promise class... ??? But I had problems doing that. Maybe it's how CoffeeScript extends things? TODO: I want a way to do 'then' and 'catch' without effecting any following 'thens' or 'caches' It's easy to implement, but what to call it? Leaning towards tapThen. If I had Ruby's 'tap', then I could do this effectively with: .tap (a) -> a.then -> but .tapThen -> is even nicer Will it be available on returned promises? (see ES6 Promise extension above) tapThen: (successF, failF) -> @then successF, failF @ # return the current promise, not the one returned from the then-call above */ defineModule(module, function() { var ArtPromise, k, v; ArtPromise = (function() { var deepAll, logPromiseProblems, noop; function ArtPromise() {} ArtPromise.isPromise = isPromise; ArtPromise.testPromise = function(promise) { promise.then(function(v) { return console.log("promise.resolve", v); }); return promise["catch"](function(v) { return console.log("promise.reject", v); }); }; ArtPromise.mapAll = function(map) { var key, keys; keys = Object.keys(map); return Promise.all((function() { var j, len, results; results = []; for (j = 0, len = keys.length; j < len; j++) { key = keys[j]; results.push(map[key]); } return results; })()).then(function(values) { var i, j, key, len, out; out = {}; for (i = j = 0, len = keys.length; j < len; i = ++j) { key = keys[i]; out[key] = values[i]; } return out; }); }; ArtPromise.containsPromises = function(plainStructure) { var containsPromises; containsPromises = false; deepEach(plainStructure, function(v) { return containsPromises || (containsPromises = isPromise(v)); }); return containsPromises; }; /* For use with Node-style callbacks: IN: (error, data) -> error: null or set if there was an error data: set if error is null Example: Promise.withCallback (callback) -> doAsyncStuff -> callback() */ ArtPromise.withCallback = function(startPromiseBodyFunction) { return new BlueBirdPromise(function(resolve, reject) { var callback; callback = function(err, data) { if (err) { return reject(new Error(err)); } return resolve(data); }; return startPromiseBodyFunction(callback); }); }; ArtPromise.newExternallyResolvable = function() { var out, p; out = {}; p = new BlueBirdPromise(function(resolve, reject) { out.resolve = resolve; return out.reject = reject; }); p.resolve = out.resolve; p.reject = out.reject; return p; }; noop = function(a) { return a; }; ArtPromise.deepAll = deepAll = function(plainStructure, resolvedResultPreprocessor) { var promises; if (resolvedResultPreprocessor == null) { resolvedResultPreprocessor = noop; } promises = []; deepEach(plainStructure, function(v) { if (isPromise(v)) { return promises.push(v); } }); return Promise.all(promises).then(function(resolved) { var i; i = 0; return deepMap(plainStructure, function(v) { if (isPromise(v)) { return resolvedResultPreprocessor(resolved[i++]); } else { return v; } }); }); }; ArtPromise.deepResolve = deepAll; /* Serializer makes it easy to ensure promise-returning functions are invoked in order, after each promise is resolved. USAGE: * EXAMPLE 1: Basic - not too different from normal Promise sequences serializer = new ArtPromise.Serializer serializer.then -> doA() * then execute sometime later, possbly asynchronously: serializer.then -> doB() * then execute sometime later, possbly asynchronously: serializer.then (doBResult) -> * doA and doB have completed and any returning promises resolved * the result of the last 'then' is passed in * EXAMPLE 2: apply the same async function serially to each element in list * - list's order is preserved * - each invocation waits for the previous one to complete serializer = new ArtPromise.Serializer list.forEach serializer.serialize f = (element) -> # do something with element, possibly returning a promise serializer.then (lastFResult) -> * do something after the last invocation of f completes * the result of the last invocation of 'f' is passed in * EXAMPLE 3: mix multiple serialized functions and manual @then invocations * - invocation order is perserved serializer = new ArtPromise.Serializer serializedA = serializer.serialize aFunction serializedB = serializer.serialize bFunction serializedB() serializer.then -> @cFunction() serializedB() serializedA() serializedB() serializer.then (lastBFunctionResult) -> * this is invoked AFTER: * evaluating, in order, waiting for any promises: * bFunction, cFunction, bFunction, aFunction, bFunction */ ArtPromise.Serializer = (function() { function Serializer() { this._lastPromise = BlueBirdPromise.resolve(); } /* Returns a new function, serializedF, that acts just like 'f' - f is forced to be async: - if f doesn't return a promise, a promise wrapping f's result is returned - invoking serializedF queues f in this serializer instance's sequence via @then IN: any function with any signature OUT: (f's signature) -> promise.then (fResult) -> Example with Comparison: * all asyncActionReturningPromise(element)s get called immediately * and may complete randomly at some later event myArray.forEach (element) -> asyncActionReturningPromise element * VS * asyncActionReturningPromise(element) only gets called * after the previous call completes. * If a previous call failes, the remaining calls never happen. serializer = new Promise.Serializer myArray.forEach serializer.serialize (element) -> asyncActionReturningPromise element * bonus, you can do things when all the promises complete: serializer.then => * or if anything fails serializer.catch => * VS - shortcut * Just insert "Promise.serialize" before your forEach function to ensure serial invocations. * However, you don't get the full functionality of the previous example. myArray.forEach Promise.serialize (element) -> asyncActionReturningPromise element */ Serializer.prototype.serialize = function(f) { return (function(_this) { return function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return _this.then(function() { return f.apply(null, args); }); }; })(this); }; Serializer.prototype.then = function(resolved, rejected) { return this._lastPromise = this._lastPromise.then(resolved, rejected); }; Serializer.prototype["catch"] = function(rejected) { return this._lastPromise = this._lastPromise["catch"](rejected); }; Serializer.prototype.always = function(f) { return this._lastPromise = this._lastPromise["catch"]((function(_this) { return function() { return null; }; })(this)).then(f); }; /* OUT: promise that resolves / rejects only when there are no more pending tasks queued with the serializer. .then (lastResult) -> .catch (lastError) -> NOTE: allDonePromise could complete, then more tasks could be queued with the serializer. Promises can't be resolved/rejected twice, so when the more-tasks complete, the first allDonePromise won't do anything. However, you can call allDonePromise again once the tasks are queued and get notified when THEY are done. */ Serializer.prototype.allDonePromise = function() { var currentLastPromise; currentLastPromise = this._lastPromise; return currentLastPromise.then((function(_this) { return function(lastResult) { if (currentLastPromise === _this._lastPromise) { return lastResult; } else { return _this.allDonePromise(); } }; })(this))["catch"]((function(_this) { return function(lastError) { if (currentLastPromise === _this._lastPromise) { throw lastError; } else { return _this.allDonePromise(); } }; })(this)); }; return Serializer; })(); /* OUT: serializedF = -> Promise.resolve f args... IN: any args EFFECT: f is invoked with args passed in AFTER the last invocation of serializedF completes. OUT: promise.then -> results from f NOTE: 'f' can return a promise, but it doesn't have to. If it does return a promise, the next 'f' invocation will not start until and if the previous one's promise completes. USAGE: serializedF = Promise.serialize f = -> # do something, possibly returning a promise serializedF() serializedF() serializedF() .then (resultOfLastF)-> * executed after f was executed and any returned promises resolved, 3 times, sequentially OR serializedF = Promise.serialize f = (element) -> # do something with element, possibly returning a promise Promise.all (serializedF item for item in list) .then (results) -> * f was excuted list.length times sequentially * results contains the result values from each execution, in order */ ArtPromise.serialize = function(f) { return new ArtPromise.Serializer().serialize(f); }; ArtPromise.logPromise = function(context, p) { var currentSecond, log, startTime; if (p == null) { p = context; context = "(context not specified)"; } log = namespace.log, currentSecond = namespace.currentSecond; log({ logPromise_start: context }); startTime = currentSecond(); return Promise.then(function() { if (isFunction(p)) { return p(); } else { return p; } }).tap(function(result) { return log({ logPromise_success: { context: context, result: result, seconds: currentSecond() - startTime } }); }).tapCatch(function(error) { return log.error({ logPromise_error: { context: context, error: error, seconds: currentSecond() - startTime } }); }); }; ArtPromise.logPromiseProblems = logPromiseProblems = function(context, p) { var currentSecond, log, startTime; log = namespace.log, currentSecond = namespace.currentSecond; startTime = currentSecond(); return Promise.then(function() { if (isFunction(p)) { return p(); } else { return p; } }).tapCatch(function(error) { return log.error({ logRejectedPromises: { context: context, error: error, seconds: currentSecond() - startTime } }); }); }; ArtPromise.logPromiseErrors = logPromiseProblems; ArtPromise.logRejectedPromises = logPromiseProblems; ArtPromise.invert = function(promise) { return promise.then(function(e) { throw new ErrorWithInfo("" + e, e); }, function(v) { return v; }); }; ArtPromise["finally"] = function(promise, action) { return BlueBirdPromise.resolve(promise)["finally"](action); }; ArtPromise.then = BlueBirdPromise["try"]; return ArtPromise; })(); for (k in ArtPromise) { v = ArtPromise[k]; BlueBirdPromise[k] || (BlueBirdPromise[k] = v); } return BlueBirdPromise; }); }).call(this); //# sourceMappingURL=Promise.js.map