UNPKG

2.84 kBJavaScriptView Raw
1'use strict';
2
3function isPromise(obj) {
4 return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
5}
6
7/**
8 * Return a function that will run a function asynchronously or synchronously
9 *
10 * example:
11 * runAsync(wrappedFunction, callback)(...args);
12 *
13 * @param {Function} func Function to run
14 * @param {Function} cb Callback function passed the `func` returned value
15 * @return {Function(arguments)} Arguments to pass to `func`. This function will in turn
16 * return a Promise (Node >= 0.12) or call the callbacks.
17 */
18
19var runAsync = module.exports = function (func, cb) {
20 cb = cb || function () {};
21
22 return function () {
23
24 var args = arguments;
25
26 var promise = new Promise(function (resolve, reject) {
27 var resolved = false;
28 const wrappedResolve = function (value) {
29 if (resolved) {
30 console.warn('Run-async promise already resolved.')
31 }
32 resolved = true;
33 resolve(value);
34 }
35
36 var rejected = false;
37 const wrappedReject = function (value) {
38 if (rejected) {
39 console.warn('Run-async promise already rejected.')
40 }
41 rejected = true;
42 reject(value);
43 }
44
45 var usingCallback = false;
46 var callbackConflict = false;
47 var contextEnded = false;
48
49 var answer = func.apply({
50 async: function () {
51 if (contextEnded) {
52 console.warn('Run-async async() called outside a valid run-async context, callback will be ignored.');
53 return function() {};
54 }
55 if (callbackConflict) {
56 console.warn('Run-async wrapped function (async) returned a promise.\nCalls to async() callback can have unexpected results.');
57 }
58 usingCallback = true;
59 return function (err, value) {
60 if (err) {
61 wrappedReject(err);
62 } else {
63 wrappedResolve(value);
64 }
65 };
66 }
67 }, Array.prototype.slice.call(args));
68
69 if (usingCallback) {
70 if (isPromise(answer)) {
71 console.warn('Run-async wrapped function (sync) returned a promise but async() callback must be executed to resolve.');
72 }
73 } else {
74 if (isPromise(answer)) {
75 callbackConflict = true;
76 answer.then(wrappedResolve, wrappedReject);
77 } else {
78 wrappedResolve(answer);
79 }
80 }
81 contextEnded = true;
82 });
83
84 promise.then(cb.bind(null, null), cb);
85
86 return promise;
87 }
88};
89
90runAsync.cb = function (func, cb) {
91 return runAsync(function () {
92 var args = Array.prototype.slice.call(arguments);
93 if (args.length === func.length - 1) {
94 args.push(this.async());
95 }
96 return func.apply(this, args);
97 }, cb);
98};