index.js 2.8 KB

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