runtime.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use strict";
  2. /*
  3. * $asyncbind has multiple uses, depending on the parameter list. It is in Function.prototype, so 'this' is always a function
  4. *
  5. * 1) If called with a single argument (this), it is used when defining an async function to ensure when
  6. * it is invoked, the correct 'this' is present, just like "bind". For legacy reasons, 'this' is given
  7. * a memeber 'then' which refers to itself.
  8. * 2) If called with a second parameter ("catcher") and catcher!==true it is being used to invoke an async
  9. * function where the second parameter is the error callback (for sync exceptions and to be passed to
  10. * nested async calls)
  11. * 3) If called with the second parameter===true, it is the same use as (1), but the function is wrapped
  12. * in an 'Promise' as well bound to 'this'.
  13. * It is the same as calling 'new Promise(this)', where 'this' is the function being bound/wrapped
  14. * 4) If called with the second parameter===0, it is the same use as (1), but the function is wrapped
  15. * in a 'LazyThenable', which executes lazily and can resolve synchronously.
  16. * It is the same as calling 'new LazyThenable(this)' (if such a type were exposed), where 'this' is
  17. * the function being bound/wrapped
  18. */
  19. function processIncludes(includes,input) {
  20. var src = input.toString() ;
  21. var t = "return "+src ;
  22. var args = src.match(/.*\(([^)]*)\)/)[1] ;
  23. var re = /['"]!!!([^'"]*)['"]/g ;
  24. var m = [] ;
  25. while (1) {
  26. var mx = re.exec(t) ;
  27. if (mx)
  28. m.push(mx) ;
  29. else break ;
  30. }
  31. m.reverse().forEach(function(e){
  32. t = t.slice(0,e.index)+includes[e[1]]+t.substr(e.index+e[0].length) ;
  33. }) ;
  34. t = t.replace(/\/\*[^*]*\*\//g,' ').replace(/\s+/g,' ') ;
  35. return new Function(args,t)() ;
  36. }
  37. var $asyncbind = processIncludes({
  38. zousan:require('./zousan').toString(),
  39. thenable:require('./thenableFactory').toString()
  40. },
  41. function $asyncbind(self,catcher) {
  42. "use strict";
  43. if (!Function.prototype.$asyncbind) {
  44. Object.defineProperty(Function.prototype,"$asyncbind",{value:$asyncbind,enumerable:false,configurable:true,writable:true}) ;
  45. }
  46. if (!$asyncbind.trampoline) {
  47. $asyncbind.trampoline = function trampoline(t,x,s,e,u){
  48. return function b(q) {
  49. while (q) {
  50. if (q.then) {
  51. q = q.then(b, e) ;
  52. return u?undefined:q;
  53. }
  54. try {
  55. if (q.pop) {
  56. if (q.length)
  57. return q.pop() ? x.call(t) : q;
  58. q = s;
  59. } else
  60. q = q.call(t)
  61. } catch (r) {
  62. return e(r);
  63. }
  64. }
  65. }
  66. };
  67. }
  68. if (!$asyncbind.LazyThenable) {
  69. $asyncbind.LazyThenable = '!!!thenable'();
  70. $asyncbind.EagerThenable = $asyncbind.Thenable = ($asyncbind.EagerThenableFactory = '!!!zousan')();
  71. }
  72. function boundThen() {
  73. return resolver.apply(self,arguments);
  74. }
  75. var resolver = this;
  76. switch (catcher) {
  77. case true:
  78. return new ($asyncbind.Thenable)(boundThen);
  79. case 0:
  80. return new ($asyncbind.LazyThenable)(boundThen);
  81. case undefined:
  82. /* For runtime compatibility with Nodent v2.x, provide a thenable */
  83. boundThen.then = boundThen ;
  84. return boundThen ;
  85. default:
  86. return function(){
  87. try {
  88. return resolver.apply(self,arguments);
  89. } catch(ex) {
  90. return catcher(ex);
  91. }
  92. }
  93. }
  94. }) ;
  95. function $asyncspawn(promiseProvider,self) {
  96. if (!Function.prototype.$asyncspawn) {
  97. Object.defineProperty(Function.prototype,"$asyncspawn",{value:$asyncspawn,enumerable:false,configurable:true,writable:true}) ;
  98. }
  99. if (!(this instanceof Function)) return ;
  100. var genF = this ;
  101. return new promiseProvider(function enough(resolve, reject) {
  102. var gen = genF.call(self, resolve, reject);
  103. function step(fn,arg) {
  104. var next;
  105. try {
  106. next = fn.call(gen,arg);
  107. if(next.done) {
  108. if (next.value !== resolve) {
  109. if (next.value && next.value===next.value.then)
  110. return next.value(resolve,reject) ;
  111. resolve && resolve(next.value);
  112. resolve = null ;
  113. }
  114. return;
  115. }
  116. if (next.value.then) {
  117. next.value.then(function(v) {
  118. step(gen.next,v);
  119. }, function(e) {
  120. step(gen.throw,e);
  121. });
  122. } else {
  123. step(gen.next,next.value);
  124. }
  125. } catch(e) {
  126. reject && reject(e);
  127. reject = null ;
  128. return;
  129. }
  130. }
  131. step(gen.next);
  132. });
  133. }
  134. // Initialize async bindings
  135. $asyncbind() ;
  136. $asyncspawn() ;
  137. // Export async bindings
  138. module.exports = {
  139. $asyncbind:$asyncbind,
  140. $asyncspawn:$asyncspawn
  141. };