Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement promises entirely in JS

  • Loading branch information...
commit 4d818f1fd3454f972ef6e0a4f32ed26585804dd5 1 parent 3414eab
@ry ry authored
View
8 lib/sys.js
@@ -81,10 +81,4 @@ exports.exec = function (command) {
* prototype
* @param {function} superCtor Constructor function to inherit prototype from
*/
-exports.inherits = function (ctor, superCtor) {
- var tempCtor = function(){};
- tempCtor.prototype = superCtor.prototype;
- ctor.super_ = superCtor.prototype;
- ctor.prototype = new tempCtor();
- ctor.prototype.constructor = ctor;
-};
+exports.inherits = process.inherits;
View
1  src/node.cc
@@ -811,7 +811,6 @@ static Local<Object> Load(int argc, char *argv[]) {
// Initialize the C++ modules..................filename of module
- Promise::Initialize(process); // events.cc
Stdio::Initialize(process); // stdio.cc
Timer::Initialize(process); // timer.cc
SignalHandler::Initialize(process); // signal_handler.cc
View
96 src/node.js
@@ -70,10 +70,6 @@ node.inherits = function () {
throw new Error("node.inherits() has moved. Use require('sys') to access it.");
};
-process.inherits = function () {
- throw new Error("process.inherits() has moved. Use require('sys') to access it.");
-};
-
process.createChildProcess = function (file, args, env) {
var child = new process.ChildProcess();
@@ -184,6 +180,24 @@ process.EventEmitter.prototype.listeners = function (type) {
return this._events[type];
};
+process.inherits = function (ctor, superCtor) {
+ var tempCtor = function(){};
+ tempCtor.prototype = superCtor.prototype;
+ ctor.super_ = superCtor.prototype;
+ ctor.prototype = new tempCtor();
+ ctor.prototype.constructor = ctor;
+};
+
+
+// Promise
+
+process.Promise = function () {
+ process.EventEmitter.call();
+ this._blocking = false;
+ this._hasFired = false;
+}
+process.inherits(process.Promise, process.EventEmitter);
+
process.Promise.prototype.timeout = function(timeout) {
if (timeout === undefined) {
return this._timeoutDuration;
@@ -235,7 +249,22 @@ process.Promise.prototype.cancel = function() {
process.Promise.prototype.emitCancel = function() {
var args = Array.prototype.slice.call(arguments);
args.unshift('cancel');
+ this.emit.apply(this, args);
+};
+
+process.Promise.prototype.emitSuccess = function() {
+ if (this.hasFired) return;
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift('success');
+ this.hasFired = true;
+ this.emit.apply(this, args);
+};
+process.Promise.prototype.emitError = function() {
+ if (this.hasFired) return;
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift('error');
+ this.hasFired = true;
this.emit.apply(this, args);
};
@@ -254,26 +283,51 @@ process.Promise.prototype.addCancelback = function (listener) {
return this;
};
+/* Poor Man's coroutines */
+var coroutineStack = [];
+
+process.Promise.prototype._destack = function () {
+ this._blocking = false;
+
+ while (coroutineStack.length > 0 &&
+ !coroutineStack[coroutineStack.length-1]._blocking)
+ {
+ coroutineStack.pop();
+ process.unloop("one");
+ }
+};
+
process.Promise.prototype.wait = function () {
+ var self = this;
var ret;
- var had_error = false;
- this.addCallback(function () {
- if (arguments.length == 1) {
- ret = arguments[0];
- } else if (arguments.length > 1) {
- ret = [];
- for (var i = 0; i < arguments.length; i++) {
- ret.push(arguments[i]);
- }
- }
- })
- .addErrback(function (arg) {
- had_error = true;
- ret = arg;
- })
- .block();
+ var hadError = false;
+
+ self.addCallback(function () {
+ if (arguments.length == 1) {
+ ret = arguments[0];
+ } else if (arguments.length > 1) {
+ ret = Array.prototype.slice.call(arguments);
+ }
+ self._destack();
+ });
+
+ self.addErrback(function (arg) {
+ hadError = true;
+ ret = arg;
+ self._destack();
+ });
+
+ coroutineStack.push(self);
+ if (coroutineStack.length > 10) {
+ process.stdio.writeError("WARNING: promise.wait() is being called too often.\n");
+ }
+ self._blocking = true;
+
+ process.loop();
+
+ process.assert(self._blocking == false);
- if (had_error) {
+ if (hadError) {
if (ret) {
throw ret;
} else {
View
138 src/node_events.cc
@@ -22,10 +22,6 @@ using namespace v8;
Persistent<FunctionTemplate> EventEmitter::constructor_template;
-/* Poor Man's coroutines */
-static Promise *coroutine_top;
-static int coroutine_stack_size;
-
void EventEmitter::Initialize(Local<FunctionTemplate> ctemplate) {
HandleScope scope;
@@ -37,9 +33,6 @@ void EventEmitter::Initialize(Local<FunctionTemplate> ctemplate) {
constructor_template->SetClassName(String::NewSymbol("EventEmitter"));
// All other prototype methods are defined in events.js
-
- coroutine_top = NULL;
- coroutine_stack_size = 0;
}
static bool ReallyEmit(Handle<Object> self,
@@ -104,135 +97,4 @@ bool EventEmitter::Emit(const char *event_s, int argc, Handle<Value> argv[]) {
return ReallyEmit(handle_, event, argc, argv);
}
-Persistent<FunctionTemplate> Promise::constructor_template;
-
-void Promise::Initialize(v8::Handle<v8::Object> target) {
- HandleScope scope;
-
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- constructor_template = Persistent<FunctionTemplate>::New(t);
- constructor_template->Inherit(EventEmitter::constructor_template);
- constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
- constructor_template->SetClassName(String::NewSymbol("Promise"));
-
- NODE_SET_PROTOTYPE_METHOD(constructor_template, "block", Block);
- NODE_SET_PROTOTYPE_METHOD(constructor_template, "emitSuccess", EmitSuccess);
- NODE_SET_PROTOTYPE_METHOD(constructor_template, "emitError", EmitError);
-
- target->Set(String::NewSymbol("Promise"),
- constructor_template->GetFunction());
-}
-
-v8::Handle<v8::Value> Promise::New(const v8::Arguments& args) {
- HandleScope scope;
-
- Promise *promise = new Promise();
- promise->Wrap(args.This());
- promise->Attach();
-
- return args.This();
-}
-
-Handle<Value> Promise::Block(const Arguments& args) {
- HandleScope scope;
- Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
- promise->Block();
- return Undefined();
-}
-
-v8::Handle<v8::Value> Promise::EmitSuccess(const v8::Arguments& args) {
- HandleScope scope;
- Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
-
- int argc = args.Length();
- Local<Value> argv[argc];
- for (int i = 0; i < argc; i++) {
- argv[i] = args[i];
- }
-
- bool r = promise->EmitSuccess(argc, argv);
-
- return r ? True() : False();
-}
-
-v8::Handle<v8::Value> Promise::EmitError(const v8::Arguments& args) {
- HandleScope scope;
- Promise *promise = ObjectWrap::Unwrap<Promise>(args.Holder());
-
- int argc = args.Length();
- Local<Value> argv[argc];
- for (int i = 0; i < argc; i++) {
- argv[i] = args[i];
- }
-
- bool r = promise->EmitError(argc, argv);
-
- return r ? True() : False();
-}
-
-void Promise::Block(void) {
- blocking_ = true;
-
- assert(prev_ == NULL);
- if (coroutine_top) prev_ = coroutine_top;
- coroutine_top = this;
- coroutine_stack_size++;
- if (coroutine_stack_size > 10) {
- fprintf(stderr, "(node) WARNING: promise.wait() is being called too often.\n");
- }
-
- ev_loop(EV_DEFAULT_UC_ 0);
-
- assert(!blocking_);
-}
-
-void Promise::Destack() {
- assert(coroutine_top == this);
- ev_unloop(EV_DEFAULT_ EVUNLOOP_ONE);
- coroutine_top = prev_;
- prev_ = NULL;
- coroutine_stack_size--;
-}
-
-void Promise::Detach(void) {
- /* Poor Man's coroutines */
- blocking_ = false;
- while (coroutine_top && !coroutine_top->blocking_) {
- coroutine_top->Destack();
- }
-
- ObjectWrap::Detach();
-}
-
-bool Promise::EmitSuccess(int argc, v8::Handle<v8::Value> argv[]) {
- if (has_fired_) return false;
-
- bool r = Emit("success", argc, argv);
-
- has_fired_ = true;
- Detach();
-
- return r;
-}
-
-bool Promise::EmitError(int argc, v8::Handle<v8::Value> argv[]) {
- if (has_fired_) return false;
-
- bool r = Emit("error", argc, argv);
-
- has_fired_ = true;
- Detach();
-
- return r;
-}
-
-Promise* Promise::Create(void) {
- HandleScope scope;
-
- Local<Object> handle =
- Promise::constructor_template->GetFunction()->NewInstance();
-
- return ObjectWrap::Unwrap<Promise>(handle);
-}
-
} // namespace node
View
36 src/node_events.h
@@ -20,41 +20,5 @@ class EventEmitter : public ObjectWrap {
EventEmitter() : ObjectWrap () { }
};
-class Promise : public EventEmitter {
- public:
- static void Initialize(v8::Handle<v8::Object> target);
-
- static v8::Persistent<v8::FunctionTemplate> constructor_template;
- static Promise* Create(void);
-
- bool EmitSuccess(int argc, v8::Handle<v8::Value> argv[]);
- bool EmitError(int argc, v8::Handle<v8::Value> argv[]);
- void Block();
-
- v8::Handle<v8::Object> Handle() {
- return handle_;
- }
-
- protected:
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
- static v8::Handle<v8::Value> Block(const v8::Arguments& args);
- static v8::Handle<v8::Value> EmitSuccess(const v8::Arguments& args);
- static v8::Handle<v8::Value> EmitError(const v8::Arguments& args);
-
- virtual void Detach(void);
-
- bool has_fired_;
- bool blocking_;
- Promise *prev_; /* for the prev in the Poor Man's coroutine stack */
-
- void Destack();
-
- Promise() : EventEmitter() {
- has_fired_ = false;
- blocking_ = false;
- prev_ = NULL;
- }
-};
-
} // namespace node
#endif // SRC_EVENTS_H_
Please sign in to comment.
Something went wrong with that request. Please try again.