Skip to content

Commit 4d818f1

Browse files
committed
Implement promises entirely in JS
1 parent 3414eab commit 4d818f1

File tree

5 files changed

+76
-203
lines changed

5 files changed

+76
-203
lines changed

lib/sys.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,4 @@ exports.exec = function (command) {
8181
* prototype
8282
* @param {function} superCtor Constructor function to inherit prototype from
8383
*/
84-
exports.inherits = function (ctor, superCtor) {
85-
var tempCtor = function(){};
86-
tempCtor.prototype = superCtor.prototype;
87-
ctor.super_ = superCtor.prototype;
88-
ctor.prototype = new tempCtor();
89-
ctor.prototype.constructor = ctor;
90-
};
84+
exports.inherits = process.inherits;

src/node.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,6 @@ static Local<Object> Load(int argc, char *argv[]) {
811811

812812

813813
// Initialize the C++ modules..................filename of module
814-
Promise::Initialize(process); // events.cc
815814
Stdio::Initialize(process); // stdio.cc
816815
Timer::Initialize(process); // timer.cc
817816
SignalHandler::Initialize(process); // signal_handler.cc

src/node.js

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,6 @@ node.inherits = function () {
7070
throw new Error("node.inherits() has moved. Use require('sys') to access it.");
7171
};
7272

73-
process.inherits = function () {
74-
throw new Error("process.inherits() has moved. Use require('sys') to access it.");
75-
};
76-
7773

7874
process.createChildProcess = function (file, args, env) {
7975
var child = new process.ChildProcess();
@@ -184,6 +180,24 @@ process.EventEmitter.prototype.listeners = function (type) {
184180
return this._events[type];
185181
};
186182

183+
process.inherits = function (ctor, superCtor) {
184+
var tempCtor = function(){};
185+
tempCtor.prototype = superCtor.prototype;
186+
ctor.super_ = superCtor.prototype;
187+
ctor.prototype = new tempCtor();
188+
ctor.prototype.constructor = ctor;
189+
};
190+
191+
192+
// Promise
193+
194+
process.Promise = function () {
195+
process.EventEmitter.call();
196+
this._blocking = false;
197+
this._hasFired = false;
198+
}
199+
process.inherits(process.Promise, process.EventEmitter);
200+
187201
process.Promise.prototype.timeout = function(timeout) {
188202
if (timeout === undefined) {
189203
return this._timeoutDuration;
@@ -235,7 +249,22 @@ process.Promise.prototype.cancel = function() {
235249
process.Promise.prototype.emitCancel = function() {
236250
var args = Array.prototype.slice.call(arguments);
237251
args.unshift('cancel');
252+
this.emit.apply(this, args);
253+
};
254+
255+
process.Promise.prototype.emitSuccess = function() {
256+
if (this.hasFired) return;
257+
var args = Array.prototype.slice.call(arguments);
258+
args.unshift('success');
259+
this.hasFired = true;
260+
this.emit.apply(this, args);
261+
};
238262

263+
process.Promise.prototype.emitError = function() {
264+
if (this.hasFired) return;
265+
var args = Array.prototype.slice.call(arguments);
266+
args.unshift('error');
267+
this.hasFired = true;
239268
this.emit.apply(this, args);
240269
};
241270

@@ -254,26 +283,51 @@ process.Promise.prototype.addCancelback = function (listener) {
254283
return this;
255284
};
256285

286+
/* Poor Man's coroutines */
287+
var coroutineStack = [];
288+
289+
process.Promise.prototype._destack = function () {
290+
this._blocking = false;
291+
292+
while (coroutineStack.length > 0 &&
293+
!coroutineStack[coroutineStack.length-1]._blocking)
294+
{
295+
coroutineStack.pop();
296+
process.unloop("one");
297+
}
298+
};
299+
257300
process.Promise.prototype.wait = function () {
301+
var self = this;
258302
var ret;
259-
var had_error = false;
260-
this.addCallback(function () {
261-
if (arguments.length == 1) {
262-
ret = arguments[0];
263-
} else if (arguments.length > 1) {
264-
ret = [];
265-
for (var i = 0; i < arguments.length; i++) {
266-
ret.push(arguments[i]);
267-
}
268-
}
269-
})
270-
.addErrback(function (arg) {
271-
had_error = true;
272-
ret = arg;
273-
})
274-
.block();
303+
var hadError = false;
304+
305+
self.addCallback(function () {
306+
if (arguments.length == 1) {
307+
ret = arguments[0];
308+
} else if (arguments.length > 1) {
309+
ret = Array.prototype.slice.call(arguments);
310+
}
311+
self._destack();
312+
});
313+
314+
self.addErrback(function (arg) {
315+
hadError = true;
316+
ret = arg;
317+
self._destack();
318+
});
319+
320+
coroutineStack.push(self);
321+
if (coroutineStack.length > 10) {
322+
process.stdio.writeError("WARNING: promise.wait() is being called too often.\n");
323+
}
324+
self._blocking = true;
325+
326+
process.loop();
327+
328+
process.assert(self._blocking == false);
275329

276-
if (had_error) {
330+
if (hadError) {
277331
if (ret) {
278332
throw ret;
279333
} else {

src/node_events.cc

Lines changed: 0 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ using namespace v8;
2222

2323
Persistent<FunctionTemplate> EventEmitter::constructor_template;
2424

25-
/* Poor Man's coroutines */
26-
static Promise *coroutine_top;
27-
static int coroutine_stack_size;
28-
2925
void EventEmitter::Initialize(Local<FunctionTemplate> ctemplate) {
3026
HandleScope scope;
3127

@@ -37,9 +33,6 @@ void EventEmitter::Initialize(Local<FunctionTemplate> ctemplate) {
3733
constructor_template->SetClassName(String::NewSymbol("EventEmitter"));
3834

3935
// All other prototype methods are defined in events.js
40-
41-
coroutine_top = NULL;
42-
coroutine_stack_size = 0;
4336
}
4437

4538
static bool ReallyEmit(Handle<Object> self,
@@ -104,135 +97,4 @@ bool EventEmitter::Emit(const char *event_s, int argc, Handle<Value> argv[]) {
10497
return ReallyEmit(handle_, event, argc, argv);
10598
}
10699

107-
Persistent<FunctionTemplate> Promise::constructor_template;
108-
109-
void Promise::Initialize(v8::Handle<v8::Object> target) {
110-
HandleScope scope;
111-
112-
Local<FunctionTemplate> t = FunctionTemplate::New(New);
113-
constructor_template = Persistent<FunctionTemplate>::New(t);
114-
constructor_template->Inherit(EventEmitter::constructor_template);
115-
constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
116-
constructor_template->SetClassName(String::NewSymbol("Promise"));
117-
118-
NODE_SET_PROTOTYPE_METHOD(constructor_template, "block", Block);
119-
NODE_SET_PROTOTYPE_METHOD(constructor_template, "emitSuccess", EmitSuccess);
120-
NODE_SET_PROTOTYPE_METHOD(constructor_template, "emitError", EmitError);
121-
122-
target->Set(String::NewSymbol("Promise"),
123-
constructor_template->GetFunction());
124-
}
125-
126-
v8::Handle<v8::Value> Promise::New(const v8::Arguments& args) {
127-
HandleScope scope;
128-
129-
Promise *promise = new Promise();
130-
promise->Wrap(args.This());
131-
promise->Attach();
132-
133-
return args.This();
134-
}
135-
136-
Handle<Value> Promise::Block(const Arguments& args) {
137-
HandleScope scope;
138-
Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
139-
promise->Block();
140-
return Undefined();
141-
}
142-
143-
v8::Handle<v8::Value> Promise::EmitSuccess(const v8::Arguments& args) {
144-
HandleScope scope;
145-
Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
146-
147-
int argc = args.Length();
148-
Local<Value> argv[argc];
149-
for (int i = 0; i < argc; i++) {
150-
argv[i] = args[i];
151-
}
152-
153-
bool r = promise->EmitSuccess(argc, argv);
154-
155-
return r ? True() : False();
156-
}
157-
158-
v8::Handle<v8::Value> Promise::EmitError(const v8::Arguments& args) {
159-
HandleScope scope;
160-
Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
161-
162-
int argc = args.Length();
163-
Local<Value> argv[argc];
164-
for (int i = 0; i < argc; i++) {
165-
argv[i] = args[i];
166-
}
167-
168-
bool r = promise->EmitError(argc, argv);
169-
170-
return r ? True() : False();
171-
}
172-
173-
void Promise::Block(void) {
174-
blocking_ = true;
175-
176-
assert(prev_ == NULL);
177-
if (coroutine_top) prev_ = coroutine_top;
178-
coroutine_top = this;
179-
coroutine_stack_size++;
180-
if (coroutine_stack_size > 10) {
181-
fprintf(stderr, "(node) WARNING: promise.wait() is being called too often.\n");
182-
}
183-
184-
ev_loop(EV_DEFAULT_UC_ 0);
185-
186-
assert(!blocking_);
187-
}
188-
189-
void Promise::Destack() {
190-
assert(coroutine_top == this);
191-
ev_unloop(EV_DEFAULT_ EVUNLOOP_ONE);
192-
coroutine_top = prev_;
193-
prev_ = NULL;
194-
coroutine_stack_size--;
195-
}
196-
197-
void Promise::Detach(void) {
198-
/* Poor Man's coroutines */
199-
blocking_ = false;
200-
while (coroutine_top && !coroutine_top->blocking_) {
201-
coroutine_top->Destack();
202-
}
203-
204-
ObjectWrap::Detach();
205-
}
206-
207-
bool Promise::EmitSuccess(int argc, v8::Handle<v8::Value> argv[]) {
208-
if (has_fired_) return false;
209-
210-
bool r = Emit("success", argc, argv);
211-
212-
has_fired_ = true;
213-
Detach();
214-
215-
return r;
216-
}
217-
218-
bool Promise::EmitError(int argc, v8::Handle<v8::Value> argv[]) {
219-
if (has_fired_) return false;
220-
221-
bool r = Emit("error", argc, argv);
222-
223-
has_fired_ = true;
224-
Detach();
225-
226-
return r;
227-
}
228-
229-
Promise* Promise::Create(void) {
230-
HandleScope scope;
231-
232-
Local<Object> handle =
233-
Promise::constructor_template->GetFunction()->NewInstance();
234-
235-
return ObjectWrap::Unwrap<Promise>(handle);
236-
}
237-
238100
} // namespace node

src/node_events.h

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,5 @@ class EventEmitter : public ObjectWrap {
2020
EventEmitter() : ObjectWrap () { }
2121
};
2222

23-
class Promise : public EventEmitter {
24-
public:
25-
static void Initialize(v8::Handle<v8::Object> target);
26-
27-
static v8::Persistent<v8::FunctionTemplate> constructor_template;
28-
static Promise* Create(void);
29-
30-
bool EmitSuccess(int argc, v8::Handle<v8::Value> argv[]);
31-
bool EmitError(int argc, v8::Handle<v8::Value> argv[]);
32-
void Block();
33-
34-
v8::Handle<v8::Object> Handle() {
35-
return handle_;
36-
}
37-
38-
protected:
39-
static v8::Handle<v8::Value> New(const v8::Arguments& args);
40-
static v8::Handle<v8::Value> Block(const v8::Arguments& args);
41-
static v8::Handle<v8::Value> EmitSuccess(const v8::Arguments& args);
42-
static v8::Handle<v8::Value> EmitError(const v8::Arguments& args);
43-
44-
virtual void Detach(void);
45-
46-
bool has_fired_;
47-
bool blocking_;
48-
Promise *prev_; /* for the prev in the Poor Man's coroutine stack */
49-
50-
void Destack();
51-
52-
Promise() : EventEmitter() {
53-
has_fired_ = false;
54-
blocking_ = false;
55-
prev_ = NULL;
56-
}
57-
};
58-
5923
} // namespace node
6024
#endif // SRC_EVENTS_H_

0 commit comments

Comments
 (0)