-
Notifications
You must be signed in to change notification settings - Fork 64
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
[WIP] Promise #526
[WIP] Promise #526
Conversation
autoload/vital/__vital__/Promise.vim
Outdated
elseif settled == s:REJECTED | ||
let CB = a:promise._rejections[i] | ||
else | ||
throw 'vital:Promise: Cannot publish a pending promise' |
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.
[vital-throw-message] reported by reviewdog 🐶
use vital: Promise:
prefix to throw message
コードレビューはまだできるレベルになってませんが,上記3点相談させてください! |
autoload/vital/__vital__/Promise.vim
Outdated
call s:_subscribe( | ||
\ a:thenable, | ||
\ v:null, | ||
\ {Val -> s:_resolve(a:promise, Val)}, |
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.
[vint] reported by reviewdog 🐶
unexpected token: > (see ynkdir/vim-vimlparser)
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.
:(
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.
oh
autoload/vital/__vital__/Promise.vim
Outdated
call s:_subscribe( | ||
\ a:thenable, | ||
\ v:null, | ||
\ {Val -> s:_resolve(a:promise, Val)}, |
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.
[vimlint] reported by reviewdog 🐶
EVP_0: unexpected token: >
👍 This also has extensibility. Or... should not we omit the result value? Is it inconvenient?
Unfortunately, there is no way to do that. I want to support it. |
コメントありがとうございます.
たしかに🦀
はい,デフォルトで
超同意なんですが,それにはこの PR が入る必要があり,デッドロック… 多分 Promise のテストのために Promise に依存した機能を使うのはテストが落ちた時に切り分けが面倒になりそうです. 非同期なテストについては @lambdalisue さんの System.Job のテストを参考にさせてもらえば良いのかもしれないです. |
@rhysd ++ let p = s:Promise.new({e -> e.resolve(42)}) 自分もこっちのがいいと思います。理由は以下2点です。
あとちなみにですが、
その必要はないはずです。 function! Succ(value)
return a:value + 1
endfunction
" echo 2
echo {resolve,reject -> resolve(1)}(function('Succ'), function('Succ')) |
確かに.cancelable promise の話は確か ES Promise では実装しないとなったと思いますが,Vim script では実装するという選択肢はありますね.@thinca さんのおっしゃるように拡張性を担保する価値がありそうです.
function! Succ(value)
return a:value + 1
endfunction
" echo 2
echo {resolve -> resolve(1)}(function('Succ'), function('Succ'))
すみません,増えた時と書かれてました…つまり
あれ,おっしゃる通りでした… 僕が勘違いしていました.ありがとうございます.ただしこれは内部実装に依存した話でドキュメントにはざっと見た感じ明記されていない気がしました. |
議論したい項目に 4. モジュール名を追加しました.個人的には |
👍 |
|
一応脳内で想定していたのは |
autoload/vital/__vital__/Promise.vim
Outdated
|
||
" Internal APIs | ||
|
||
" TODO: v:null or 0, which should the default result value be ? |
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.
👍 for v:null
Codecov Report
@@ Coverage Diff @@
## master #526 +/- ##
==========================================
+ Coverage 78.53% 78.65% +0.11%
==========================================
Files 71 72 +1
Lines 8406 8545 +139
==========================================
+ Hits 6602 6721 +119
- Misses 1804 1824 +20
Continue to review full report at Codecov.
|
質問項目の 1. について考えてみたんですが,やはり
Vim script のラムダ式の引数は function s:resolveFoo(resolve, ...)
" do something ...
let foo = ...
resolve(foo)
endfunction みたいな感じで |
知らなかった… |
1be6b86
to
fc10a6d
Compare
elseif settled == s:REJECTED | ||
let CB = a:promise._rejections[i] | ||
else | ||
throw 'vital:Promise: Cannot publish a pending promise' |
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.
[vital-throw-message] reported by reviewdog 🐶
use vital: Async.Promise:
prefix to throw message
call s:_subscribe( | ||
\ a:thenable, | ||
\ v:null, | ||
\ {result -> s:_resolve(a:promise, result)}, |
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.
[vint] reported by reviewdog 🐶
unexpected token: > (see ynkdir/vim-vimlparser)
call s:_subscribe( | ||
\ a:thenable, | ||
\ v:null, | ||
\ {result -> s:_resolve(a:promise, result)}, |
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.
[vimlint] reported by reviewdog 🐶
EVP_0: unexpected token: >
elseif settled == s:REJECTED | ||
let CB = a:promise._rejections[i] | ||
else | ||
throw 'vital:Promise: Cannot publish a pending promise' |
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.
[vital-throw-message] reported by reviewdog 🐶
use vital: Async.Promise:
prefix to throw message
elseif settled == s:REJECTED | ||
let CB = a:promise._rejections[i] | ||
else | ||
throw 'vital:Async.Promise: Cannot publish a pending promise' |
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.
[vital-throw-message] reported by reviewdog 🐶
use vital: Async.Promise:
prefix to throw message
elseif settled == s:REJECTED | ||
let CB = a:promise._rejections[i] | ||
else | ||
throw 'vital:Async.Promise: Cannot publish a pending promise' |
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.
[vital-throw-message] reported by reviewdog 🐶
use vital: Async.Promise:
prefix to throw message
elseif settled == s:REJECTED | ||
let CB = a:promise._rejections[i] | ||
else | ||
throw 'vital:Promise: Cannot publish a pending promise' |
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.
[vital-throw-message] reported by reviewdog 🐶
use vital: Async.Promise:
prefix to throw message
elseif settled == s:REJECTED | ||
let CB = a:promise._rejections[i] | ||
else | ||
throw 'vital:Async.Promise: Cannot publish a pending promise' |
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.
[vital-throw-message] reported by reviewdog 🐶
use vital: Async.Promise:
prefix to throw message
Vim の maxdepth 難しい。 話は違うかもしれませんが gina だと job の callback の中で sleep 1m を呼ぶと、その部分から別のコールバックが呼ばれたように扱われてしまい maxdepth に到達したことがあります(sleep で処理が別に渡るので、その部分から job の別のコールバックが呼ばれる形になって再起になるから maxdepth に到達する) 上記があるのでもしかすると then の中で sleep 呼ぶと同じ現象になるかもしれません。 |
なるほど partial 使ったほうが良さげかもですね.下記の順序で修正してみます:
ちなみに上記実装では funcmaxdepth の問題は(指摘いただいている |
ありがとうございますー |
はい、WIP 外れたらでお願いします(いくつか修正すべき点があるのは認識済みです) |
ざっくりベンチマーク取ってみたのですが、
partial 使った版も見てみましたがほぼ変わりませんでした(タイマー発火部分が支配的っぽい) |
フライングすみません 🙇 💦 |
個人的には非同期の時と同期の時とで、ベンチマークを比較することにあんまり意味ないかなと思います(メインスレッドの忙しさで14倍以上になりえるので)。 どちらかというと then のチェインが実行されている間でもカーソルが動かせる等、メインスレッドを占有していないか?の方が今回の場合は重要な気がします(そして、それを定量的に測るのは難しすぎる) |
↑ にしても 14 倍はちょっと‥‥と思うところもありますねw |
10000回の 14倍と言っても,1回の |
because it is usually shorter and faster. (As long as I measured, partial is 10% faster than lambda when some variable is captured.
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
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.
何回言うねん
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
elseif has_callback && success | ||
call s:_resolve(a:promise, Result) | ||
elseif !success | ||
call s:_reject(a:promise, Err) |
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.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
I created another PR #567 to review this library because this PR's timeline is too long. Please leave review comments there. |
In Vim8 or Neovim, some asynchronous operations are added like job, channel or timer. They're recommended because async operations don't block user input. However, we don't want to see nested callbacks like good old JavaScript. In ES6 or later,
Promise
is one of the answers to callbacks hell.APIs are almost the same as ECMAScript (ECMA-262) spec. And MDN is also a good documentation for Promise. (Japanese or English)
Promise is a unidirectional state machine like following figure (pending state will move to fulfilled or rejected, but fulfilled or rejected states cannot move to pending):
I ported ES6 promise to Vim script. I finished only library implementation. Tests and docs should be added. But I created this PR because I want to discuss some points before writing tests and docs. I referred es6-promise a lot, which is the most popular JavaScript Promise polyfill.
Supported APIs are below:
s:Promise.new(resolver)
makes a new promise value (corresponding tonew Promise(resolver)
in JS.s:Promise.resolve(val)
s:Promise.reject(val)
{promise value}.then(on_fulfilled[, on_rejected])
{promise value}.catch(on_rejected)
s:Promise.all(array)
s:Promise.race(array)
s:Promise.is_promise(val)
returnsval
is a promise or not as booleans:Promise.is_available()
returns whether this module is available or not in current environmentbluebird has more nice APIs. But I add only standard APIs in this PR.
Note that this library is currently only for Vim8 because internally lambda expressions are used. I need to rewrite them with
Partial
later. I'm considering to support Neovim and Vim8+.Japanese blogpost
1.
s:Promise.new
In JavaScript,
new Promise((resolve, reject) => ...)
works nicely.However, in Vim script, we need to start argument with upper case as below because parameters of the lambda are funcref and not prefixed withFunc ref arguments of lambda can start with lower case.a:
.I feel this does not look not so good. My idea is changing API of
s:Promise.new
as following.Here, the
e
is an 'executor', which has fulfillment function and rejection function as its properties. (As bonus of this idea, I guess Vim7 can also be supported.) However, this interface is different from JS. It may be confusing for the people who knows JS's promise. What do you think?2. Default result value of fulfillment
In JavaScript, default fulfilled result is
undefined
as below.However, Vim script does not have such a good(?) default value like that. Currently I use
v:null
for the purpose. However0
may be more suitable because:function
's default value is0
.3. How to test async operations with vim-themis
This is just a question. How can I test async operations with vim-themis? I could not find an interface for them. For example, mocha can wait async results with
done
callback.4. Module name
Currently I put this module as
Vital.Promise
. But it may not be appropriate. Candidates are below.Vital.Promise
(for now)Vital.Async.Promise
(may consider other asynchronous types such as observable)Vital.Data.Promise
(but is promise a data structure? 'data structure' is so fuzzy word...)Both English and Japanese comments are welcome! Give me your thoughts.