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
Async/await experiment #2221
Merged
Merged
Async/await experiment #2221
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
9b2b285
Async/await experiment
hmdne 8cef252
More work on async/await
hmdne 2215a6f
Vendored-minitest: Remove unneeded files, port to async/await to supp…
hmdne 58a1694
Ensure we use PromiseV2 on await things; require "async" became requi…
hmdne d28905b
Revert "Remove PromiseV2 tests for now, as we have no way to handle t…
hmdne 78dea62
Queue all code from top nodes
hmdne 05a471e
Implement Kernel#sleep (it's async, so you need to await it)
hmdne 4d9cc49
Correct specs and tests to work with await and promises:
hmdne 4460c7b
Disable async/await by default
hmdne 7a1c137
Support auto-await
hmdne aa52606
Async/await/promisev2 documentation
hmdne 57c8d13
Allow to disable the warnings about an experimental nature of the new…
hmdne 2568fee
Promise: Add an alias to PromiseV1 (we forgot about it somehow). Add …
hmdne 7253a8c
#async to become #__async__; wildcard support
hmdne ec4b268
Support for require(x).__await__ to await an async require
hmdne 00ccedf
Correct await in eval; tests for await
hmdne 09397df
Fix a few more cases for await
hmdne 4f57623
Async/await in classes and modules
hmdne e540fee
Add {Proc,Method}#async? to check if a block is async
hmdne File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains 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,6 @@ | ||
module.exports = { | ||
"extends": "./.eslintrc.js", | ||
"parserOptions": { | ||
"ecmaVersion": 8 | ||
}, | ||
}; |
This file contains 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 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 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,109 @@ | ||
# Asynchronous code (PromiseV2 / async / await) | ||
|
||
Please be aware that this functionality is marked as experimental and may change | ||
in the future. | ||
|
||
In order to disable the warnings that will be shown if you use those experimental | ||
features, add the following line before requiring `promise/v2` or `await` and after | ||
requiring `opal`. | ||
|
||
```ruby | ||
`Opal.config.experimental_features_severity = 'ignore'` | ||
``` | ||
|
||
## PromiseV2 | ||
|
||
In Opal 1.2 we introduced PromiseV2 which is to replace the default Promise in Opal 2.0 | ||
(which will become PromiseV1). Right now it's experimental, but the interface of PromiseV1 | ||
stay unchanged and will continue to be supported. | ||
|
||
It is imperative that during the transition period you either `require 'promise/v1'` or | ||
`require 'promise/v2'` and then use either `PromiseV1` or `PromiseV2`. | ||
|
||
If you write library code it's imperative that you don't require the promise itself, but | ||
detect if `PromiseV2` is defined and use the newer implementation, for instance using the | ||
following code: | ||
|
||
```ruby | ||
module MyLibrary | ||
Promise = defined?(PromiseV2) ? PromiseV2 : ::Promise | ||
end | ||
``` | ||
|
||
The difference between `PromiseV1` and `PromiseV2` is that `PromiseV1` is a pure-Ruby | ||
implementation of a Promise, while `PromiseV2` is reusing the JavaScript `Promise`. Both are | ||
incompatible with each other, but `PromiseV2` can be awaited (see below) and they translate | ||
1 to 1 to the JavaScript native `Promise` (they are bridged; you can directly return a | ||
`Promise` from JavaScript API without a need to translate it). The other difference is that | ||
`PromiseV2` always runs a `#then` block a tick later, while `PromiseV1` would could run it | ||
instantaneously. | ||
|
||
## Async/await | ||
|
||
In Opal 1.3 we implemented the CoffeeScript pattern of async/await. As of now, it's hidden | ||
behind a magic comment, but this behavior may change in the future. | ||
|
||
Example: | ||
|
||
```ruby | ||
# await: true | ||
|
||
require "await" | ||
|
||
def wait_5_seconds | ||
puts "Let's wait 5 seconds..." | ||
sleep(5).await | ||
puts "Done!" | ||
end | ||
|
||
wait_5_seconds.__await__ | ||
``` | ||
|
||
It's important to understand what happens under the hood: every scope in which `#__await__` is | ||
encountered will become async, which means that it will return a Promise that will resolve | ||
to a value. This includes methods, blocks and the top scope. This means, that `#__await__` is | ||
infectious and you need to remember to `#__await__` everything along the way, otherwise | ||
a program will finish too early and the values may be incorrect. | ||
|
||
[You can take a look at how we ported Minitest to support asynchronous tests.](https://github.com/opal/opal/pull/2221/commits/8383c7b45a94fe4628778f429508b9c08c8948b0) Take note, that | ||
it was ported to use `#await` while the finally accepted version uses `#__await__`. | ||
|
||
It is certainly correct to `#__await__` any value, including non-Promises, for instance | ||
`5.__await__` will correctly resolve to `5` (except that it will make the scope an async | ||
function, with all the limitations described above). | ||
|
||
The `await` stdlib module includes a few useful functions, like async-aware `each_await` | ||
function and `sleep` that doesn't block the thread. It also includes a method `#await` | ||
which is an alias of `#itself` - it makes sense to auto-await that method. | ||
|
||
This approach is certainly incompatible with what Ruby does, but due to a dynamic nature | ||
of Ruby and a different model of JavaScript this was the least invasive way to catch up | ||
with the latest JavaScript trends and support `Promise` heavy APIs and asynchronous code. | ||
|
||
## Auto-await | ||
|
||
The magic comment also accepts a comma-separated list of methods to be automatically | ||
awaited. An individual value can contain a wildcard character `*`. For instance, | ||
those two blocks of code are equivalent: | ||
|
||
```ruby | ||
# await: true | ||
|
||
require "await" | ||
|
||
[1,2,3].each_await do |i| | ||
p i | ||
sleep(i).__await__ | ||
end.__await__ | ||
``` | ||
|
||
```ruby | ||
# await: sleep, *await* | ||
|
||
require "await" | ||
|
||
[1,2,3].each_await do |i| | ||
p i | ||
sleep i | ||
end | ||
``` |
This file contains 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 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 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 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 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 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 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 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 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 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.
No, it can't be done as a magic comment:
eslint/eslint#14854