-
Notifications
You must be signed in to change notification settings - Fork 7
/
waitfor.js
156 lines (119 loc) · 6.89 KB
/
waitfor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* node wait.for-ES6 - based on ES6-harmony generators
- Sequential programming for node.js - end of callback hell
- Copyright 2013 Lucio Tato
-- WARNING: bleeding edge --
This lib is based on ECMAScript 6,
the next version of the javascript standard, code-named "Harmony".
(release target date December 2013).
This lib also uses bleeding edge v8 Harmony features, so you’ll need to
use the latest -unstable- nodejs version wich today is v0.11.6
and also pass the --harmony flag when executing node.
node --harmony waitfor-demo.js
If you want to use "wait.for" but you can't use (unstable) node and/or ES6-Harmony
you can use the Fibers-based version of wait.for
at: http://github.com/luciotato/waitfor
The Fibers-based version of wait.for only requires node-fibers(stable), and node >= 0.5.2
*/
"use strict";
var Wait = {
launchFiber: function(generatorFn){ // wait.launchFiber(fn,arg1,arg2...)
//console.log(Array.prototype.slice.call(arguments));
// starts a generator (emulating a fiber)
// helper function, call the async, using theCallback as callback
function callTheAsync(FunctionAndArgs,theCallback){
// prepare arguments
var argsOnly=Array.prototype.slice.call(FunctionAndArgs,1); // remove function from args & convert to Array
argsOnly.push(theCallback); //add callback to arguments
// start the asyncFn
FunctionAndArgs[0].apply(null, argsOnly); // call the asyncFn, (strict: this=null)
}
// launchFiber :
//console.log('launchFiber',arguments);
// get parameters
var newArgs=Array.prototype.slice.call(arguments,1); // remove generatorFn from args & convert to Array
var finalCallback=null; //optional
if (typeof generatorFn !== 'function') {
if (generatorFn==='onComplete') { //1st param is string 'onComplete'
//special mode: second parameter is finalCallback tp signal completion of the iterator
finalCallback=newArgs.shift(); //remove 2nd param finalCallback
generatorFn=newArgs.shift(); //remove. 3rd param is generatorFn
//console.log('launchFiber mode:async ',newArgs);
}
else {
console.log(arguments);
throw new Error('first argument must be a function*, not '+(typeof generatorFn));
}
}
// create the iterator
var thisIterator = generatorFn.apply(null, newArgs); // create the iterator.
// Note: calling a generator function, DOES NOT execute it.
// just creates an iterator
// create a closure to act as callback for the async calls in this iterator
// this callback will RESUME the generator, returning 'data' as the result of the 'yield' keyword
thisIterator.defaultCallback=function(err,data){
// on callback:
// console.log('on callback, err:',err,'data:',data);
// if err, schedule a throw inside the generator, and exit.
if (err) return thisIterator.throw(err);
// data:
// return data as the result of the yield keyword,
// and RESUME the generator. (we store the result of resuming the generator in 'nextPart')
var nextPart = thisIterator.next(data); // thisIterator.next => RESUME generator, data:->result of 'yield'
//after the next part of the generator runs...
if (nextPart.done) {//the generator function has finished (executed "return")
finalCallback && finalCallback(null, {result:nextPart.value, iterator:thisIterator}); //final callback ok
return; //it was the last part, nothing more to do
}
// else...
// not finished yet. The generator paused on another yield,
// with another call to: wait.for(asyncFn,arg1,arg2...)
// so in nextPart.value we have wait.for's arguments
// Let's call the indicated async
callTheAsync(nextPart.value, thisIterator.defaultCallback);
// the async function callback will be handled by this same closure (thisIterator.defaultCallback)
// repeating the loop until nextPart.done
};
// launching the Fiber:
// RUN first part of generator
thisIterator.defaultCallback();
return thisIterator; //launchFiber returns created iterator
}
,for: function(asyncFn){ // wait.for(asyncFn,arg1,arg2,...)
return arguments;
// the syntax is: yield wait.for(asyncFn,arg1,arg2...)
// so, the generator will yield wait.for's result (ie: it's arguments)
// to the calling function (launchFiber / defaultCallback)
// given that wait.for is basically: function(){return arguments;}
// then, the expression wait.for(asyncFn,arg1,arg2...) ~= [asyncFn,arg1,arg2...]
// so, you can completely omit 'wait.for' and use
// "the fancy syntax": yield [asyncFn, arg1, arg2...]
}
,forMethod: function(obj,methodName){ // wait.forMethod(dbObj,'select',....)
var method=obj[methodName];
if (!method) throw new Error('wait.forMethod: second argument must be the async method name (string)');
var newArgs=Array.prototype.slice.call(arguments,1); // remove obj from args
newArgs[0]=method.bind(obj); // create a function wich will call obj.method (with this=obj)
return newArgs; // continue as with non-method async
}
,runGenerator: function(generatorFn){ // yield [wait.asyncFiber, generatorFn,arg1,arg2,...)
// helper closure asyncFiber
function async_launchFiber(generatorFnAndParams, finalCallback){ //
try{
generatorFnAndParams.unshift('onComplete',finalCallback); // insert 'onComplete',finalCallback
//console.log('async_launchFiber',generatorFnAndParams);
Wait.launchFiber.apply(null, generatorFnAndParams) // wait.launchFiber, call with all params
// 'onComplete' makes Wait.launchFiber to call finalCallback at generator:done
// so asyncFiber can signal completion
}
catch(err){
console.log('ERR async_launchFiber',err.message);
return finalCallback(err);
}
}
var newArgs=Array.prototype.slice.call(arguments); // convert to array
return [async_launchFiber,newArgs]; //compose array with helper fn asyncFiber
// return [asyncFiber,[generatorFn,arg1,arg2,...]]
// so wait.for will call: Wait.asyncFiber([generatorFn,arg1,arg2,...], callback)
}
};
module.exports = Wait; //export