Browse Source

Implement promises entirely in JS

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
4d818f1fd3
  1. 8
      lib/sys.js
  2. 1
      src/node.cc
  3. 96
      src/node.js
  4. 138
      src/node_events.cc
  5. 36
      src/node_events.h

8
lib/sys.js

@ -81,10 +81,4 @@ exports.exec = function (command) {
* prototype * prototype
* @param {function} superCtor Constructor function to inherit prototype from * @param {function} superCtor Constructor function to inherit prototype from
*/ */
exports.inherits = function (ctor, superCtor) { exports.inherits = process.inherits;
var tempCtor = function(){};
tempCtor.prototype = superCtor.prototype;
ctor.super_ = superCtor.prototype;
ctor.prototype = new tempCtor();
ctor.prototype.constructor = ctor;
};

1
src/node.cc

@ -811,7 +811,6 @@ static Local<Object> Load(int argc, char *argv[]) {
// Initialize the C++ modules..................filename of module // Initialize the C++ modules..................filename of module
Promise::Initialize(process); // events.cc
Stdio::Initialize(process); // stdio.cc Stdio::Initialize(process); // stdio.cc
Timer::Initialize(process); // timer.cc Timer::Initialize(process); // timer.cc
SignalHandler::Initialize(process); // signal_handler.cc SignalHandler::Initialize(process); // signal_handler.cc

96
src/node.js

@ -70,10 +70,6 @@ node.inherits = function () {
throw new Error("node.inherits() has moved. Use require('sys') to access it."); 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) { process.createChildProcess = function (file, args, env) {
var child = new process.ChildProcess(); var child = new process.ChildProcess();
@ -184,6 +180,24 @@ process.EventEmitter.prototype.listeners = function (type) {
return this._events[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) { process.Promise.prototype.timeout = function(timeout) {
if (timeout === undefined) { if (timeout === undefined) {
return this._timeoutDuration; return this._timeoutDuration;
@ -235,7 +249,22 @@ process.Promise.prototype.cancel = function() {
process.Promise.prototype.emitCancel = function() { process.Promise.prototype.emitCancel = function() {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
args.unshift('cancel'); 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); this.emit.apply(this, args);
}; };
@ -254,26 +283,51 @@ process.Promise.prototype.addCancelback = function (listener) {
return this; 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 () { process.Promise.prototype.wait = function () {
var self = this;
var ret; var ret;
var had_error = false; var hadError = false;
this.addCallback(function () {
if (arguments.length == 1) { self.addCallback(function () {
ret = arguments[0]; if (arguments.length == 1) {
} else if (arguments.length > 1) { ret = arguments[0];
ret = []; } else if (arguments.length > 1) {
for (var i = 0; i < arguments.length; i++) { ret = Array.prototype.slice.call(arguments);
ret.push(arguments[i]); }
} self._destack();
} });
})
.addErrback(function (arg) { self.addErrback(function (arg) {
had_error = true; hadError = true;
ret = arg; ret = arg;
}) self._destack();
.block(); });
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) { if (ret) {
throw ret; throw ret;
} else { } else {

138
src/node_events.cc

@ -22,10 +22,6 @@ using namespace v8;
Persistent<FunctionTemplate> EventEmitter::constructor_template; Persistent<FunctionTemplate> EventEmitter::constructor_template;
/* Poor Man's coroutines */
static Promise *coroutine_top;
static int coroutine_stack_size;
void EventEmitter::Initialize(Local<FunctionTemplate> ctemplate) { void EventEmitter::Initialize(Local<FunctionTemplate> ctemplate) {
HandleScope scope; HandleScope scope;
@ -37,9 +33,6 @@ void EventEmitter::Initialize(Local<FunctionTemplate> ctemplate) {
constructor_template->SetClassName(String::NewSymbol("EventEmitter")); constructor_template->SetClassName(String::NewSymbol("EventEmitter"));
// All other prototype methods are defined in events.js // All other prototype methods are defined in events.js
coroutine_top = NULL;
coroutine_stack_size = 0;
} }
static bool ReallyEmit(Handle<Object> self, 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); 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 } // namespace node

36
src/node_events.h

@ -20,41 +20,5 @@ class EventEmitter : public ObjectWrap {
EventEmitter() : 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 } // namespace node
#endif // SRC_EVENTS_H_ #endif // SRC_EVENTS_H_

Loading…
Cancel
Save