1 | 'use strict';
|
---|
2 |
|
---|
3 | function 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 |
|
---|
19 | var 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 |
|
---|
90 | runAsync.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 | };
|
---|