-
Notifications
You must be signed in to change notification settings - Fork 700
Eliminate promise polling #854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| #ifndef ASYNC_BATON | ||
| #define ASYNC_BATON | ||
|
|
||
| #include <uv.h> | ||
| #include <nan.h> | ||
|
|
||
| // Base class for Batons used for callbacks (for example, | ||
| // JS functions passed as callback parameters, | ||
| // or field properties of configuration objects whose values are callbacks) | ||
| struct AsyncBaton { | ||
| uv_async_t req; | ||
|
|
||
| bool done; | ||
| }; | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| #ifndef PROMISE_COMPLETION | ||
| #define PROMISE_COMPLETION | ||
|
|
||
| #include <nan.h> | ||
|
|
||
| #include "async_baton.h" | ||
|
|
||
| // PromiseCompletion forwards either the resolved result or the rejection reason | ||
| // to the native layer, once the promise completes | ||
| // | ||
| // inherits ObjectWrap so it can be used in v8 and managed by the garbage collector | ||
| // it isn't wired up to be instantiated or accessed from the JS layer other than | ||
| // for the purpose of promise result forwarding | ||
| class PromiseCompletion : public Nan::ObjectWrap | ||
| { | ||
| // callback type called when a promise completes | ||
| typedef void (*Callback) (bool isFulfilled, AsyncBaton *baton, v8::Local<v8::Value> resultOfPromise); | ||
|
|
||
| static NAN_METHOD(New); | ||
| static NAN_METHOD(PromiseFulfilled); | ||
| static NAN_METHOD(PromiseRejected); | ||
|
|
||
| // persistent handles for NAN_METHODs | ||
| static Nan::Persistent<v8::Function> newFn; | ||
| static Nan::Persistent<v8::Function> promiseFulfilled; | ||
| static Nan::Persistent<v8::Function> promiseRejected; | ||
|
|
||
| static v8::Local<v8::Value> Bind(Nan::Persistent<v8::Function> &method, v8::Local<v8::Object> object); | ||
| static void CallCallback(bool isFulfilled, const Nan::FunctionCallbackInfo<v8::Value> &info); | ||
|
|
||
| // callback and baton stored for the promise that this PromiseCompletion is | ||
| // attached to. when the promise completes, the callback will be called with | ||
| // the result, and the stored baton. | ||
| Callback callback; | ||
| AsyncBaton *baton; | ||
|
|
||
| void Setup(v8::Local<v8::Function> thenFn, v8::Local<v8::Value> result, AsyncBaton *baton, Callback callback); | ||
| public: | ||
| // If result is a promise, this will instantiate a new PromiseCompletion | ||
| // and have it forward the promise result / reason via the baton and callback | ||
| static bool ForwardIfPromise(v8::Local<v8::Value> result, AsyncBaton *baton, Callback callback); | ||
|
|
||
| static void InitializeComponent(); | ||
| }; | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| #include "../include/promise_completion.h" | ||
|
|
||
| Nan::Persistent<v8::Function> PromiseCompletion::newFn; | ||
| Nan::Persistent<v8::Function> PromiseCompletion::promiseFulfilled; | ||
| Nan::Persistent<v8::Function> PromiseCompletion::promiseRejected; | ||
|
|
||
| // initializes the persistent handles for NAN_METHODs | ||
| void PromiseCompletion::InitializeComponent() { | ||
| v8::Local<v8::FunctionTemplate> newTemplate = Nan::New<v8::FunctionTemplate>(New); | ||
| newTemplate->InstanceTemplate()->SetInternalFieldCount(1); | ||
| newFn.Reset(newTemplate->GetFunction()); | ||
|
|
||
| promiseFulfilled.Reset(Nan::New<v8::FunctionTemplate>(PromiseFulfilled)->GetFunction()); | ||
| promiseRejected.Reset(Nan::New<v8::FunctionTemplate>(PromiseRejected)->GetFunction()); | ||
| } | ||
|
|
||
| bool PromiseCompletion::ForwardIfPromise(v8::Local<v8::Value> result, AsyncBaton *baton, Callback callback) | ||
| { | ||
| Nan::HandleScope scope; | ||
|
|
||
| // check if the result is a promise | ||
| if (result->IsObject()) { | ||
| Nan::MaybeLocal<v8::Value> maybeThenProp = Nan::Get(result->ToObject(), Nan::New("then").ToLocalChecked()); | ||
| if (!maybeThenProp.IsEmpty()) { | ||
| v8::Local<v8::Value> thenProp = maybeThenProp.ToLocalChecked(); | ||
| if(thenProp->IsFunction()) { | ||
| // we can be reasonably certain that the result is a promise | ||
|
|
||
| // create a new v8 instance of PromiseCompletion | ||
| v8::Local<v8::Object> object = Nan::NewInstance(Nan::New(newFn)).ToLocalChecked(); | ||
|
|
||
| // set up the native PromiseCompletion object | ||
| PromiseCompletion *promiseCompletion = ObjectWrap::Unwrap<PromiseCompletion>(object); | ||
| promiseCompletion->Setup(thenProp.As<v8::Function>(), result, baton, callback); | ||
|
|
||
| return true; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| // creates a new instance of PromiseCompletion, wrapped in a v8 object | ||
| NAN_METHOD(PromiseCompletion::New) { | ||
| PromiseCompletion *promiseCompletion = new PromiseCompletion(); | ||
| promiseCompletion->Wrap(info.This()); | ||
| info.GetReturnValue().Set(info.This()); | ||
| } | ||
|
|
||
| // sets up a Promise to forward the promise result via the baton and callback | ||
| void PromiseCompletion::Setup(v8::Local<v8::Function> thenFn, v8::Local<v8::Value> result, AsyncBaton *baton, Callback callback) { | ||
| this->callback = callback; | ||
| this->baton = baton; | ||
|
|
||
| v8::Local<v8::Object> promise = result->ToObject(); | ||
|
|
||
| v8::Local<v8::Object> thisHandle = handle(); | ||
|
|
||
| v8::Local<v8::Value> argv[2] = { | ||
| Bind(promiseFulfilled, thisHandle), | ||
| Bind(promiseRejected, thisHandle) | ||
| }; | ||
|
|
||
| // call the promise's .then method with resolve and reject callbacks | ||
| Nan::Callback(thenFn).Call(promise, 2, argv); | ||
| } | ||
|
|
||
| // binds an object to be the context of the function. | ||
| // there might be a better way to do this than calling Function.bind... | ||
| v8::Local<v8::Value> PromiseCompletion::Bind(Nan::Persistent<v8::Function> &function, v8::Local<v8::Object> object) { | ||
| Nan::EscapableHandleScope scope; | ||
|
|
||
| v8::Local<v8::Function> bind = | ||
| Nan::Get(Nan::New(function), Nan::New("bind").ToLocalChecked()) | ||
| .ToLocalChecked().As<v8::Function>(); | ||
|
|
||
| v8::Local<v8::Value> argv[1] = { object }; | ||
|
|
||
| return scope.Escape(bind->Call(Nan::New(function), 1, argv)); | ||
| } | ||
|
|
||
| // calls the callback stored in the PromiseCompletion, passing the baton that | ||
| // was provided in construction | ||
| void PromiseCompletion::CallCallback(bool isFulfilled, const Nan::FunctionCallbackInfo<v8::Value> &info) { | ||
| v8::Local<v8::Value> resultOfPromise; | ||
|
|
||
| if (info.Length() > 0) { | ||
| resultOfPromise = info[0]; | ||
| } | ||
|
|
||
| PromiseCompletion *promiseCompletion = ObjectWrap::Unwrap<PromiseCompletion>(info.This()->ToObject()); | ||
|
|
||
| (*promiseCompletion->callback)(isFulfilled, promiseCompletion->baton, resultOfPromise); | ||
| } | ||
|
|
||
| NAN_METHOD(PromiseCompletion::PromiseFulfilled) { | ||
| CallCallback(true, info); | ||
| } | ||
|
|
||
| NAN_METHOD(PromiseCompletion::PromiseRejected) { | ||
| CallCallback(false, info); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like these should bind to
resultand notthisHandle. They are instance properties ofresultand we're rebinding the context of them.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
promiseFulfilledandpromiseRejectedare instance properties ofPromiseCompletionobjects - https://github.com/srajko/nodegit/blob/8dae154aa00b5c23f482b746ba4efcfb524a812d/generate/templates/manual/src/promise_completion.cc#L13-L14 https://github.com/srajko/nodegit/blob/8dae154aa00b5c23f482b746ba4efcfb524a812d/generate/templates/manual/src/promise_completion.cc#L97-L103We need to bind
thisHandleto them so that they can access thePromiseCompletioninstance here - https://github.com/srajko/nodegit/blob/8dae154aa00b5c23f482b746ba4efcfb524a812d/generate/templates/manual/src/promise_completion.cc#L92-L94There might be a better way to do the binding though - using the JS
Function.bindmethod was the best I could come up with, short of leaking memory by creating a function template for each instance ofPromiseCompletion:-)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I was mistaken about what was going on. I thought this was related to how the
Promise.resolvefunctions passed in now were being bound to themselves in JS. Apparently that is a requirement of Chromium and is Promise library implementation specific. The actualthenlibrary does not have this requirement and that was the backing lib fornodegit-promisewhich is why we didn't need to bind the functions being passed in during tests. With that resolved I'm good with this PR.