Implement promises entirely in JS
This commit is contained in:
parent
3414eab2f3
commit
4d818f1fd3
@ -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;
|
||||
|
@ -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
|
||||
|
100
src/node.js
100
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;
|
||||
};
|
||||
|
||||
process.Promise.prototype.wait = function () {
|
||||
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();
|
||||
/* Poor Man's coroutines */
|
||||
var coroutineStack = [];
|
||||
|
||||
if (had_error) {
|
||||
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 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 (hadError) {
|
||||
if (ret) {
|
||||
throw ret;
|
||||
} else {
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
Loading…
x
Reference in New Issue
Block a user