-
-
Notifications
You must be signed in to change notification settings - Fork 51
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
Simplify JSPromise API #115
Conversation
Time Change: +590ms (6%) 🔍 Total Time: 8,978.5ms
ℹ️ View Unchanged
|
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.
While it's true that arbitrary user code returning a promise may not be type-safe, we know that most if not all promise-returning Web APIs can only fail with JSError
and only resolve to a single type. If a user doesn't know what type to expect they can specify their expected type as JSPromise<JSValue, JSValue>
.
While it could make sense to shift the reponsibility for dynamic casts to users, I'm not sure if it's an improvement on balance from the ergonomics perspective. This change would break a lot of things in OpenCombineJS and make Publisher
conformance on promise-returning APIs harder to implement. For example, how would DOMKit promise-returning APIs like fetch
look in DOMKit if this change is merged? I'm also interested to look what impact this has on async
/await
APIs in the end?
I don't have a strong opinion here to reject the change outright though. I'm interested to hear opinions on this from other people in the meantime.
My explanation was too short, sorry. My opinion is that JSPromise should be as primitive as possible. The current JSPromise has two responsibilities as a bridge to the Promise API and as a continuation monad. Promise API: The responsibility for bridging Promise API is to provide an ability to register asynchronous tasks in the JavaScript event loop. Continuation monad: The responsibility for continuation monad is to provide There are some problems when these two responsibilities are implemented on top of the Promise API.
The main purpose of my change is to make JSPromise's responsibility only to bridge the Promise API, and to delegate the responsibility as a type-safe continuation monad to another type like Combine.Future. In this way, the user doesn't need to interact with JavaScript to
Libraries that provide type-safe APIs, such as DOMKit, should not return JSPromise directly to the user, but convert it to a type that provides type-safe continuation monad like Combine.Future.
This doesn't directly affect the async/await APIs, it's just a point I noticed while implementing the Concurrency feature. |
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.
Thanks for the explanation. This makes sense 👍
a closure that your code should call to either resolve or reject this `JSPromise` instance. | ||
*/ | ||
public convenience init(resolver: @escaping (@escaping (Result<Success, JSError>) -> ()) -> ()) { | ||
public convenience init(resolver: @escaping (@escaping (Result<JSValue, JSValue>) -> ()) -> ()) { |
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.
Could these be JSValueConvertible
so users can do something like resolve(.success("Hello, world!"))
?
public convenience init(resolver: @escaping (@escaping (Result<JSValue, JSValue>) -> ()) -> ()) { | |
public convenience init(resolver: @escaping (@escaping (Result< JSValueConvertible, JSValueConvertible >) -> ()) -> ()) { |
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.
ConvertibleToJSValue
is not conforming to Error
, so Result
can't have Failure as ConvertibleToJSValue
😢
public init?(from value: JSValue) { | ||
guard let object = value.object, object.isInstanceOf(Self.constructor) else { return nil } | ||
guard let object = value.object else { return nil } | ||
self.init(from: object) | ||
} | ||
|
||
public init?(from object: JSObject) { | ||
guard object.isInstanceOf(Self.constructor) else { return nil } | ||
self.init(unsafelyWrapping: object) | ||
} |
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.
Nice!
Co-authored-by: Jed Fox <git@jedfox.com>
This reverts commit a85ee3f.
@j-f1 Could you take a look again? |
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.
Sorry for the delay!
Changes
Remove generic type parameters from
JSPromise
These type parameters are type-safe only when it's produced by
JSPromise.then
and in other cases, it's always not type-safe. So I removed those type parameters.Remove
then
andcatch
overloadsThose overloads made it difficult to solve compile errors 😢
Add
Promise.resolve
andPromise.reject