-
-
Notifications
You must be signed in to change notification settings - Fork 146
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
Implement AsyncInterop\Promise #78
Changes from 11 commits
6701e92
82e0d4b
4b42c60
91ab8a4
d10bc89
ab4b4de
cb77cb5
7924b23
53ce643
b9c5183
40fa021
c2fbbcf
b7efdff
0c8a3fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ Table of Contents | |
* [map()](#map) | ||
* [reduce()](#reduce) | ||
* [PromisorInterface](#promisorinterface) | ||
* [AsyncInterop\Promise compatibility](#asyncinteroppromise-compatibility) | ||
4. [Examples](#examples) | ||
* [How to use Deferred](#how-to-use-deferred) | ||
* [How promise forwarding works](#how-promise-forwarding-works) | ||
|
@@ -151,6 +152,11 @@ and an associated value, or rejection (failure) and an associated reason. | |
Once in the fulfilled or rejected state, a promise becomes immutable. | ||
Neither its state nor its result (or error) can be modified. | ||
|
||
This interface extends the | ||
[`AsyncInterop\Promise`](https://github.com/async-interop/promise) interface. | ||
See [`AsyncInterop\Promise` compatibility](#asyncinteroppromise-compatibility), | ||
for further information. | ||
|
||
#### Implementations | ||
|
||
* [Promise](#promise-1) | ||
|
@@ -402,6 +408,10 @@ Creates a promise for the supplied `$promiseOrValue`. | |
If `$promiseOrValue` is a value, it will be the resolution value of the | ||
returned promise. | ||
|
||
If `$promiseOrValue` is a | ||
[`AsyncInterop\Promise`](https://github.com/async-interop/promise), a trusted | ||
promise that follows the state of the `AsyncInterop\Promise` is returned. | ||
|
||
If `$promiseOrValue` is a thenable (any object that provides a `then()` method), | ||
a trusted promise that follows the state of the thenable is returned. | ||
|
||
|
@@ -512,6 +522,27 @@ The `React\Promise\PromisorInterface` provides a common interface for objects | |
that provide a promise. `React\Promise\Deferred` implements it, but since it | ||
is part of the public API anyone can implement it. | ||
|
||
### AsyncInterop\Promise compatibility | ||
|
||
The [`PromiseInterface`](#promiseinterface) extends the | ||
[`AsyncInterop\Promise`](https://github.com/async-interop/promise) interface and | ||
thereby all promise implementations from React/Promise implement its `when()` | ||
method. | ||
|
||
The `AsyncInterop\Promise` interface is intended for interoperability with other | ||
libraries and their combinator and conversion functions. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: "The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kelunik While I understand your point, I actually prefer the current version (which is slightly more conservative and less enthusiastic). After all, while we all aim for this, "allows interoperability" has yet to be proven :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsor Can we add a very simple example to show how this interoperability may look like from a consumer perspective? Such as a dummy combinator function with the proper typehint etc.? See also above for the documentation and/or example on how we can consume an AsyncInteropPromise. |
||
|
||
Note, that although the interface is named *Promise*, it has nothing to do with | ||
promises commonly known from the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd reword this paragraph. While the interop promise may have nothing to do with the concrete interface of A+, it's still the same thing. A placeholder representing the result of an async operation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kelunik While I understand your point, I actually prefer the current version from a consumer perspective who is familiar with the term "promise" from the other projects enlisted below. I like the "A placeholder representing the result of an async operation." though 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @clue I understand your point, but I'm not sure whether it really helps consumers to understand it. They'll just think "Why the hell is it called promise if it's something entirely different?!" and be more confused than before. We had a really long discussion about the naming in async-interop/promise#4 and another one to switch back to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think this is a very important point here: To me, this whole PR seems to convey the notion that this library implements a concept commonly known as "promises", while async-interop/promise only offer a low-level API that aims to ease interoperability between different promise vendors.
This isn't exactly transparent to me either, so I'd very much appreciate if you happen to find this and could link to this here 👍
I understand the reasoning – but would disagree from a consumer perspective. If there's project A (e.g. this project) that describes "promises" and the interop standard that describes a different API then it's not the same from a consumer perspective. This means that project A should either ditch its description and adopt the interop API or the other way around (which I don't see happening any time soon). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @clue @kelunik Here is the related PR changing the name back to Promise: async-interop/promise#15 |
||
[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A), | ||
[Promises/A+](https://promisesaplus.com) or | ||
[ECMAScript 2015 (ES6)](http://www.ecma-international.org/ecma-262/6.0/#sec-promise-constructor) | ||
specifications. | ||
|
||
If you're using React/Promise as your primary value placeholder implementation, | ||
it is recommended to **not** use the `when()` method directly but the methods | ||
provided by the [`PromiseInterface`](#promiseinterface). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be reworded to: "If you use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From a consumer perspective, I actually prefer the explicit recommendation against There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, doesn't the explicit recommendation against it make you wonder even more why it exists? The alternatives are in no way more "correct" than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for my wording, "correct" wasn't intended to mean the alternative is "incorrect", but rather "not recommended and/or endorsed".
I guess this is the culprit here: Why would react/promise endorse a low-level API to its consumers if it offers a (conceived or otherwise) more powerful and comfortable higher-level API? See also the comments below 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It totally depends on what's consumer for you. Is it just application developers or also library developers? The latter will probably want to use In fact, you might want to adapt <?php
require __DIR__ . "/vendor/autoload.php";
$deferred = new React\Promise\Deferred;
$deferred->promise()->done(function ($value) {
var_dump("value", $value);
}, function ($reason) {
throw new Exception;
var_dump("reason", $reason);
});
try {
// shouldn't throw here, instead forward to the interop error handler
$deferred->reject("foobar");
} catch (Exception $e) {
var_dump("catch", $e);
} Sorry for the lengthy discussion here, should I open a separate issue for that one? It depends on this PR, but I think it could make sense for 3.0. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsor You're right about My main point is only about this statement from your README:
Because in fact, those errors aren't uncatchable, they bubble up to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, they are catchable if resolution happens synchronously. catchable is a bit misleading here as it means uncatchable by any handler, not (only) from try/catch.
Nothing prevents you from transferring the error to the ErrorHandler from inside done(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has really become quite lengthy now. Want to continue in chat? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might hop in when time allows. But, we're not going to change the behavior of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsor Bluebird for one doesn't let errors from |
||
|
||
Examples | ||
-------- | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,11 @@ | |
{"name": "Jan Sorgalla", "email": "[email protected]"} | ||
], | ||
"require": { | ||
"php": ">=5.4.0" | ||
"php": ">=5.4.0", | ||
"async-interop/promise": "^0.4.0" | ||
}, | ||
"require-dev": { | ||
"async-interop/promise-test": "^0.4.1", | ||
"phpunit/phpunit": "~4.8" | ||
}, | ||
"autoload": { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
namespace React\Promise\AsyncInterop; | ||
|
||
use AsyncInterop\Promise\Test; | ||
use React\Promise\Deferred; | ||
|
||
class DeferredTest extends Test | ||
{ | ||
public function promise(callable $canceller = null) | ||
{ | ||
$d = new Deferred($canceller); | ||
|
||
return [ | ||
$d->promise(), | ||
[$d, 'resolve'], | ||
[$d, 'reject'] | ||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
namespace React\Promise\AsyncInterop; | ||
|
||
use AsyncInterop\Promise\Test; | ||
use React\Promise\Deferred; | ||
use React\Promise\LazyPromise; | ||
|
||
class LazyPromiseTest extends Test | ||
{ | ||
public function promise(callable $canceller = null) | ||
{ | ||
$d = new Deferred($canceller); | ||
|
||
$factory = function () use ($d) { | ||
return $d->promise(); | ||
}; | ||
|
||
return [ | ||
new LazyPromise($factory), | ||
[$d, 'resolve'], | ||
[$d, 'reject'] | ||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
namespace React\Promise\AsyncInterop; | ||
|
||
use AsyncInterop\Promise\Test; | ||
use React\Promise\Promise; | ||
|
||
class PromiseTest extends Test | ||
{ | ||
public function promise(callable $canceller = null) | ||
{ | ||
$resolveCallback = $rejectCallback = null; | ||
|
||
$promise = new Promise(function ($resolve, $reject) use (&$resolveCallback, &$rejectCallback) { | ||
$resolveCallback = $resolve; | ||
$rejectCallback = $reject; | ||
}, $canceller); | ||
|
||
return [ | ||
$promise, | ||
$resolveCallback, | ||
$rejectCallback | ||
]; | ||
} | ||
} |
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.
Can we add a very simple example how we can consume an AsyncInteropPromise? Such as passing this through the
resolve()
function etc.?