Conversation
There was a problem hiding this comment.
[vimlint] reported by reviewdog 🐶
EVL104: variable may not be initialized on some execution path: l:Err
There was a problem hiding this comment.
この警告は問題ないことを確認済みですが,出ないようにコード直したほうが良いでしょうか?
There was a problem hiding this comment.
提案
lint なので強制力は無いですが、出ないほうが望ましいと思います。
特に Result と合わせる必要もないので Err の代わりに v:exception を直接使えばいいかなという気がします
There was a problem hiding this comment.
ですね,そうしておきます.ありがとうございます.
There was a problem hiding this comment.
v:exception は catch した節の中でしか使えないのを忘れていました…
|
#526 からレビューしてくれそうな気配がある人を Reviewers にアサインしてみました.年末で忙しいところすみませんが,レビューいただけると助かります. |
|
🎉 来週までには見ます! |
lambdalisue
left a comment
There was a problem hiding this comment.
とりあえず Promise.vim だけ見ました。
いくつか「提案」と「確認」を書いたのでフィードバックお願いします。
|
|
||
| " @vimlint(EVL103, 1, a:resolve) | ||
| " @vimlint(EVL103, 1, a:reject) | ||
| function! s:noop(resolve, reject) abort |
There was a problem hiding this comment.
メモ
すでに covimerage 側で修正されているかもしれませんが gina.vim に covimerage を導入した際に空っぽの関数があるとパースエラーが出ました。gina.vim では関数内部にコメントを一行書くことで対処しました。念のため
There was a problem hiding this comment.
coverage ちゃんと取れてるっぽいです💪
There was a problem hiding this comment.
提案
lint なので強制力は無いですが、出ないほうが望ましいと思います。
特に Result と合わせる必要もないので Err の代わりに v:exception を直接使えばいいかなという気がします
There was a problem hiding this comment.
確認
map に lambda 式を使うと 1.5 倍ほど遅くなりますがよろしいでしょうか?
https://gist.github.com/lambdalisue/7cbaa0ece9496ec8052f5a86d33d8f17#file-test-vim
ただ最速の expr タイプはわかりにくくなるので all で想定する promises 数がそんなに多くないのであれば許容範囲だと思います。
There was a problem hiding this comment.
知りませんでした… 文字列版も検討してみます.ここは all() の引数なので,そこまで要素数が多くなることを想定していませんが,それでも速いに越したことは無いですし.
| function! s:_race(promises, resolve, reject) abort | ||
| for p in a:promises | ||
| call p.then(a:resolve, a:reject) | ||
| endfor |
There was a problem hiding this comment.
確認
ここも同様に map(..., 'expr') タイプの方が 1.5 倍ほど早いですがよろしいでしょうか?
https://gist.github.com/lambdalisue/7cbaa0ece9496ec8052f5a86d33d8f17#file-test-vim
There was a problem hiding this comment.
ベンチマークを測った上で検討してみます.正直,.then のオーバーヘッドのほうが大きくてほぼ影響ないんじゃないかという気もしています.
There was a problem hiding this comment.
上記のベンチマークは 100000 回のループなので無視しても良いと思っています。
そのため「確認」としました
| endif | ||
| return child | ||
| endfunction | ||
| let s:PROMISE.then = function('s:_promise_then') |
There was a problem hiding this comment.
質問
辞書に対して直接関数定義を行わないで、一度スクリプト関数として定義してから function で funcref 化して辞書に代入しているのには何か理由があるのでしょうか?
There was a problem hiding this comment.
ここは以前ツイッターで @haya14busa さんに指摘されたのですが,辞書関数だとエラーが出た時のコールバックの関数名が数字になってしまってデバッグが困難になるためです.
There was a problem hiding this comment.
なるほど納得。でもデバッグの事情の為にこういうハックするのは少しモニョりますね。今回のケースだと非同期なのでデバッグの簡潔さを重要視した方が良いですが‥‥
There was a problem hiding this comment.
コードが若干複雑になってしまうのは確かにあると思います.
There was a problem hiding this comment.
実は vital core でもやってます
vital.vim/autoload/vital/vital.vim
Line 76 in e553b10
書くの面倒だという以外はいいことしかないと思ってる
| " States of promise | ||
| let s:PENDING = 0 | ||
| let s:FULFILLED = 1 | ||
| let s:REJECTED = 2 |
There was a problem hiding this comment.
提案
ECMA script では Promise.race()で Promise の状態取得が出来ますが Vim script は関数呼び出しのオーバーヘッドが大きいので上記の定数を s:_vital_created(module) を使ってモジュール定数として定義し s:PROMISE._state を公開アトリビュートとする(必要であれば lockvar も?)のはどうでしょうか?
" 使用感イメージ
let p1 = Promise.resolve()
let p2 = Promise.reject('foobar')
let p3 = Promise.new({ resolve -> execute('echo "Do nothing"') })
echo p1.state == Promise.FULFILLED
echo p2.state == Promise.REJECTED
echo p3.state == Promise.PENDINGThere was a problem hiding this comment.
すみません,#526 に1文だけ書いてこの PR には書いていなかったのですが,この PR は ES6 の Promise の API の実装にとどめる形にさせてください.理由は,
- bluebird のような強力な API を実装することも考えられるため,今入れたい API を言い始めるとキリが無さそう
- まずは最小の状態で master にマージして実戦投入して,real world で本当に必要な API だけを足したい
ためです.PR の本文にも追記しておきます💪
なのでここからは一応オフトピですが,せっかく提案いただいたので僕の考えを書いておきます.
一般的に,非同期処理で実行の状態を見て処理を分岐するのはバッドプラクティスだと思います.例えば JavaScript には VM 上で他の非同期に走っているコンテキストの状態を取る手段はありませんし,Go ではあえてゴルーチン(coroutine)の状態や ID,今自分がどのゴルーチン(コンテキスト)で実行されているかを取る API を提供していません.
また,飽くまで僕自身の経験としてですが,Promise の状態を取りたいと思ったことが無いというのもあります.
ただ,bluebird のように Promise の 3rd party 実装では Promise の状態を同期的に introspection できるものもあるので,real world で本当に必要なユースケースが発生した時に入れることを考えて議論するのが良いかなと思います.
There was a problem hiding this comment.
提案
辞書関数ではなく wait_group インスタンスを第一引数に持つ通常関数にしませんか?以下理由
wait_groupの定義位置と離れているのでselfが何を指しているのかパッと理解できない- Vim script だと
.によるアトリビュート検索も結構遅かった気がするのでpartialで変数化してNotify(i, v)みたいに呼び出したほうが繰り返し時に早そう(パフォーマンス測定はしていません)
There was a problem hiding this comment.
なるほど.特に 1. はその通りですし,ここで辞書関数にする必要は実際無さそうなので普通の関数にします.ありがとうございます.
| endfunction | ||
|
|
||
| function! s:_race(promises, resolve, reject) abort | ||
| for p in a:promises |
There was a problem hiding this comment.
あれ p は s:PROMISE オブジェクトのコピー(インスタンス)な気がします。
There was a problem hiding this comment.
@lambdalisue さんのおっしゃる通り,p は dict なので小文字で大丈夫です.
There was a problem hiding this comment.
同名のグローバル関数が定義されていた場合にエラーにならないように l:Result がいいと思います
There was a problem hiding this comment.
同名のグローバル関数が定義されていた場合にエラーにならないように l:Result がいいと思います
There was a problem hiding this comment.
同名のグローバル関数があってもローカル変数が優先されてエラーにならないようにみえます.7.4 だとそうなるのでしょうか?
There was a problem hiding this comment.
7.4.729 でも試してみましたが結果は上のものと変わりませんでした
There was a problem hiding this comment.
同名のグローバル関数が定義されていた場合にエラーにならないように l:Err がいいと思います
There was a problem hiding this comment.
(コメント書いた diff が outdated で閉じちゃったのでこちらにも)
同名のグローバル関数があってもローカル変数が優先されてエラーにならないようにみえます.7.4.729 までは同じ挙動のようなのですが,もっと古いものだとそうなったりするのでしょうか?
There was a problem hiding this comment.
これだとアウトです(参照ではなく let での定義時に l: が付いてないとエラー)
https://wandbox.org/permlink/vubuQFFNBkoSp47g
There was a problem hiding this comment.
v:exeption は関数ではないので関係ないのでは? Result は関数になりうりそうなので l: つけておいてもよさそう。
あと関連して Err が funcref にならないなら大文字にする必要はないとおもいます. 逆に funcref なら大文字かつ l: はつけるべき。
There was a problem hiding this comment.
@tyru なるほど…ありがとうございます.そういうことでしたか.
@haya14busa 確かに例外として投げられるのは文字列なので Err のほうは err で大丈夫そうですね.誤解生みそうなので修正します
There was a problem hiding this comment.
同名のグローバル関数が定義されていた場合にエラーにならないように l:Res がいいと思います
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
execute('echo e') か execute('echo ' . string(e)) ?
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
execute('echo v') か execute('echo ' . string(v)) ?
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
execute('echo v') か execute('echo ' . string(v)) ?
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
execute('echo reason') か execute('echo ' . string(reason)) ?
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
. が足らないです。
execute('echom "Failed to clone: " . ' . string(err), '')
これでもいいかも?
execute('echom "Failed to clone:" ' . string(err), '')
|
僕はフィードバックの内容と今後の変更内容(予定)に特に意見はないので Approve にしておきます。 |
| \ code ? reject(buf.err) : resolve(buf.out) | ||
| \ }, | ||
| \ })}) | ||
| endfunction |
There was a problem hiding this comment.
自分も詳しく job の仕様を把握してませんが、試した限りだと exit_cb が close_cb よりも後に実行されるとは限らないようです。
https://gist.github.com/tyru/0ffee5ba2b8cab7390f2652c9a97bc24
上記の結果 (messages) だと exit_cb のが先に実行されてしまっているため、 buf が空になってしまっています。
close_cb のみ指定して s:read_to_buf() に resolve してもらうとかですかね? (それでいいという確証はないです…)
There was a problem hiding this comment.
私の環境だとコールバックの発火順は exit_cb のほうが後なので,どうやらプラットフォーム依存ですね…ちなみに close_cb だけだと exit status が分からないのでコマンドが失敗したかどうか分からないです.
ちゃんとやるならどちらの順序で発火しても動くようにすべきですが,それをやると example としてイマイチになってしまう気がします(本質的でないコードが増えすぎる).なにか良い修正案とかないでしょうか… @lambdalisue @thinca @mattn
There was a problem hiding this comment.
プロセスの終了とチャネルのクローズはそもそも別の物なので例として不適かと思います。
プロセスが終了した後に全ての出力が欲しいのであれば、オプションに 'drop': 'never' を渡してチャネルから ch_read で持ってくるのが良いかと思います
There was a problem hiding this comment.
あと、プロセスの終了はどうでもよくて出力が全部欲しい場合は Vim のヘルプにある通り close_cb でやるべきですかね。
http://vim-jp.org/vimdoc-ja/channel.html#read-in-close-cb
個人的には実用面で言えば「プロセスの出力結果がほしい」が要求であって「プロセスが終了してから出力結果がほしい」ではないと思います。なので例としては、こちらのほうが適切かと思います。
There was a problem hiding this comment.
お手数ですが,これで動いているか確認いただけませんでしょうか?
自分の環境では元のコードも動いてしまっていたので,これで問題が解決したのかどうかが確認できず…
let s:Promise = vital#vital#import('Async.Promise')
function! s:read(chan, part) abort
let out = ''
while ch_status(a:chan, {'part' : a:part}) ==# 'buffered'
let out .= ch_read(a:chan, {'part' : a:part})
endwhile
return out
endfunction
function! s:sh(...) abort
let cmd = join(a:000, ' ')
let buf = {}
return s:Promise.new({resolve, reject -> job_start(cmd, {
\ 'drop' : 'never',
\ 'close_cb' : {ch -> 'do nothing' },
\ 'exit_cb' : {ch, code ->
\ code ?
\ reject(s:read(ch, 'err')) :
\ resolve(s:read(ch, 'out'))
\ },
\ })})
endfunction
call s:sh('ls', '-l')
\.then({out -> execute('echom ' . string('Output: ' . out), '')})
\.catch({err -> execute('echom ' . string('Error: ' . err), '')})ちなみに,close_cb は指定しないと job の開始に失敗してしまいました
There was a problem hiding this comment.
@rhysd 動きました。ただいくつか修正した方が良さそうな点がありました。
openの場合も考慮するch_status(a:chan, {'part' : a:part}) =~# 'open\|buffered'
- 末尾に改行入れる
let out .= ch_read(a:chan, {'part' : a:part}) . "\n"
:echomだと改行を含む文字列の場合つらい感じになるので:echoのがいいかもです (最後の.then()と.catch()の中)
ちなみに,
close_cbは指定しないと job の開始に失敗してしまいました
これ自分の環境 (WSL) でも再現しました。詳しく追ってないですが Error: みたいに err が空文字で渡ってきちゃいますね。
There was a problem hiding this comment.
Twitter でも言いましたが、このコードだとエラーにならなかったです。
もしかして job 以外の所でコケて .catch() に来てるかも?
https://gist.github.com/tyru/4707726873f3e4955f331109f9dc0734
There was a problem hiding this comment.
私の手元で確認した限りでは channel status が fail になっていたので job の開始に失敗しているんじゃないかと思います.他の箇所で例外が起きているなら err が空になるのは(Promise の実装がバグってない限り)おかしいですね
There was a problem hiding this comment.
動きました。修正点としては @tyru さんと同意見で、以下がいいかなと思います。
let s:Promise = vital#vital#import('Async.Promise')
function! s:read(ch, part) abort
let out = []
while ch_status(a:ch, {'part' : a:part}) =~# '^\%(open\|buffered\)$'
call add(out, ch_read(a:ch, {'part': a:part}))
endwhile
return join(out, "\n")
endfunction
function! s:sh(...) abort
let cmd = join(a:000, ' ')
return s:Promise.new({resolve, reject -> job_start(cmd, {
\ 'drop' : 'never',
\ 'close_cb' : {ch -> 'do nothing' },
\ 'exit_cb' : {ch, code ->
\ code ?
\ reject(s:read(ch, 'err')) :
\ resolve(s:read(ch, 'out'))
\ },
\ })})
endfunction
call s:sh('ls', '-l')
\.then({out -> execute('echo ' . string('Output: ' . out), '')})
\.catch({err -> execute('echo ' . string('Error: ' . err), '')})あと s:sh('cat', '/foo/bar') でちゃんと Error: cat: /foo/bar: No such file or directory が出ました。
環境: macOS High Sierra
|
let P = vital#vital#import('Async.Promise')
let ps = map(range(1000), {i -> g:P.new({res -> timer_start(i, res)})})
" race()
let amount = 0.0
let total = 10
let i = 0
while i < 10
let stamp = reltime()
call P.race(ps)
let amount += str2float(reltimestr(reltime(stamp)))
let i += 1
endwhile
echom 'amount: ' . string(amount)
echom 'avg: ' . string(amount / 10.0)
" all()
let amount = 0.0
let total = 10
let i = 0
while i < 10
let stamp = reltime()
call P.all(ps)
let amount += str2float(reltimestr(reltime(stamp)))
let i += 1
endwhile
echom 'amount: ' . string(amount)
echom 'avg: ' . string(amount / 10.0)
|
僕のベンチでも 100000 回とかでやっと差が出てる感じなので把握した上で |
|
@lambdalisue 了解です.もし今後実際に使ってみてそこで問題があることが分かったらその時に対処しましょう:+1: |
|
|
| endif | ||
| return child | ||
| endfunction | ||
| let s:PROMISE.then = function('s:_promise_then') |
There was a problem hiding this comment.
実は vital core でもやってます
vital.vim/autoload/vital/vital.vim
Line 76 in e553b10
書くの面倒だという以外はいいことしかないと思ってる
There was a problem hiding this comment.
v:exeption は関数ではないので関係ないのでは? Result は関数になりうりそうなので l: つけておいてもよさそう。
あと関連して Err が funcref にならないなら大文字にする必要はないとおもいます. 逆に funcref なら大文字かつ l: はつけるべき。
There was a problem hiding this comment.
これだとスタックトレースの情報 (v:throwpoint) が消えてしまうのでエラーオブジェクト作って v:exeption と v:throwpoint 両方持たせておいたほうがよさそう?
There was a problem hiding this comment.
[追記補足] これ"よさそう?"って微妙なカンジで最初は書いてましたが、help の github_issues の例とか実は僕も動かなくてデバックしてて、スタックトレースだしてもムズい(lambdaをおえない...)のにエラーだけだとほんとによくわからないエラーしかでないことがあるので、真剣に検討してもいいかなぁと思います。
There was a problem hiding this comment.
うーん,それはエラーオブジェクトを定義する必要が出てしまいますね…例外が起きたら v:exception だけでなくて,v:throwpoint も結合させた文字列で reject するとかどうでしょう?
There was a problem hiding this comment.
僕は @haya14busa さんのいうエラーオブジェクトのほうが良いかと思います。
分離されたデータをライブラリに勝手に結合されてイラッとした経験結構多いので……(例: stdout と stderr を勝手に結合するやつとか)
There was a problem hiding this comment.
{ 'message': v:exception [, 'throwpoint': v:throwpoint]} くらいでどうでしょうか. 文字列結合はできるなら避けたい
There was a problem hiding this comment.
nvim よく追えてないですが lambda は割りと最近入ってるはずなのでチェックとしてどれくらい機能してるか微妙かも?
has('lambda') とかのほうがいいかなと思います. あとは v:t_ や timer_startも使ってはいるので 8 以上っていうのは残してもよいかな?
There was a problem hiding this comment.
うーん,なるほど.確かにそのとおりですね.僕も具体的にどのバージョンかまでは把握していないです… 機能ベースで言うと,has('lambda') && has('timers') のほうが良さそうですね
There was a problem hiding this comment.
+1 to has('lambda') && has('timers') .
一般的にも機能ベースでチェックして、それでもできないならバージョンでチェックするというのがいいと思います
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
上下に echoあって実行できそうにみえるし call Promise or call s:Promise にしておいてもよさそう。
この help 全体的に call なしで Promise.new.... としているけどわざとですかね?
せっかくなら call つけてほぼそのまま実行できるくらいにしておいてもよいかなと思いますがどっちでもよさそう
あと execute('...', '') にしないとこの結果を確認できないので直しておいてあげると読者に優しそうですかね。
There was a problem hiding this comment.
はい,第2引数は各所で忘れていたので修正します…ありがとうございます. :call も追加します
There was a problem hiding this comment.
[補足] 面倒そうなので一度 s:echom() とか定義してもいいかなともちょっと感じましたがおまかせします
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
nice catch です.misspell さん頼む…
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
return s:Promise.new({resolve -> timer_start(a:ms, resolve)}) ですかね。 resolve できてない
There was a problem hiding this comment.
[nit] total じゃなくて remaining にしてデクリメントしていって0ならresolveってすると一つ変数を減らせる説 (total, resolved -> remaining)
There was a problem hiding this comment.
あー,確かに.キーの数は減らしたいのでそうしてみます.
There was a problem hiding this comment.
[nit] results とかのほうがわかりやすい気がしますがどうでしょう? done って done フラグ感がある(Go脳かもしれない) これもes6-promise では results だった
|
|
||
| function! s:resolve(...) abort | ||
| let promise = s:new(s:NOOP) | ||
| call s:_resolve(promise, a:0 > 0 ? a:1 : v:null) |
There was a problem hiding this comment.
[nit] get(a:000, 0, v:null) とか他のところで使ってるし書き方統一してもよさそう。
(個人的には get(a:, 1, v:null) が好みだけど統一されてたらなんでもok)
There was a problem hiding this comment.
[感想] 自分がいちばん好みじゃないの来た...! (勿論直してほしいとかではなくて単なる感想で、言い分としてはa:0は考えることが増えるのと(a:0が数でぇ...+1を見に行く...)、ほかの get or default イディオムと合わせたいので自分は get(a:, n, default) を使ってる (g: とかと一緒のイディオムにする)
There was a problem hiding this comment.
[optional] call timer_start(0, ...) っていうイディオム関数化しておいてもよさそう? asap とか next_tick とか?
読者が暗黙に timer_start(0,...) がどういう意味かわかってるという仮定をおいてるけど関数化されててコメントもあったりすると可読性よさそう
There was a problem hiding this comment.
とても気持ちはわかるのですが
- ユーザー定義関数のオーバーヘッドが大きい
- 無駄にコールスタックを積んでしまう
の二点から今のままが良いと思います。
と同じで Vim script の事情から本来不要なハックや最適化が必要になるの少しモニョりますが、仕方ない……
There was a problem hiding this comment.
その最適化が本当に必要な箇所かどうかが結構あやしいとは思いますが(2はなんかchainしすぎるとダメとか見た気がするのでわからんでもない), まぁoptionalですね。
There was a problem hiding this comment.
はい。ここがユーザーが触る場所ならば僕も関数でラップする事には同意なのですが、ライブラリ内部のコードで(個人の主観としては)ほぼイディオム的な使い方なのでラップする必要が薄いと思います。
There was a problem hiding this comment.
提案ありがとうございます.個人的な感覚では timer_start(0, ...) のままで良いかなと思います.0秒のタイマーでも意味は通じると思いますし,ラップして関数1つ増やす保守コストに見合うほどではないなと思いました.
JavaScript では確かに asap とか next_tick みたいにラップすることが多いのですが,それは環境によって下が setImmediate() だったり setTimeout() だったりするからなのですよね(使えるなら setImmediate のほうが良いため)
There was a problem hiding this comment.
このチェックはfor loop の外で settled 変数に入れたあと or 前くらいがよさそう
There was a problem hiding this comment.
publish の呼ばれ方的にこのパスが呼ばれることがない(防衛的に入れてた)ので気づきませんでしたが,確かにそうですね.ループ前にチェックするようにします.
test/Async/Promise.vimspec
Outdated
There was a problem hiding this comment.
[nit][optional] 実装のほうにも似たコメント書きましたが done より result 感がある?
There was a problem hiding this comment.
確かに.フラグをセットするんじゃなくて値をセットしてるとこは result にしといたほうが値の意図が明確で良さそうです
| Assert Equals(p._state, PENDING) | ||
| let p = p.then({-> extend(l, {'done' : 1})}) | ||
| Assert Equals(p._state, PENDING) | ||
| Assert Equals(done, 0) |
There was a problem hiding this comment.
あってるので別によさそうだけど, call s:wait_has_key(l, 'done') してあげないとフェアじゃないかも?
...と思ったけど無駄にunit test が長くなるのもアレだしムズカシイ
There was a problem hiding this comment.
(見直すとそもそも wait_has_key は done すでに定義してるので使えなかった.... ので sleep 1ms とかするとフェア感?(本当か?)
There was a problem hiding this comment.
ここは待たなくても timer_start でキューイングされた処理は後のどこかで無害な感じで実行されて終わるだけなので,待たなくて良いかなと
There was a problem hiding this comment.
あーすみません,このコメントの意図を読み違えてました..then がタイマーを挟むようになった今,確かにある程度待たないと .then が発火していないことを確認するのにはこれでは不十分でした.ちなみに .then は永遠に発火しないので,done が定義済みかによらず wait_has_key は使えないです.
test/Async/Promise.vimspec
Outdated
There was a problem hiding this comment.
wait_has_key の前でまだ Done には値入ってないことを確認すると "asynchronously" なことがわかりやすくてよさそう
test/Async/Promise.vimspec
Outdated
There was a problem hiding this comment.
ここも wait_has_key のまえにチェック入れたほうが asynchronouslyのテストであることがわかりやすくてよさそう?
test/Async/Promise.vimspec
Outdated
There was a problem hiding this comment.
このテスト catch 使ってないし Describe .then のとこにあったほうがよさそう
There was a problem hiding this comment.
これは .then のとこに置くべきなのを間違えてますね…ありがとうございます!
|
今気が付きましたが 8f6b866 は Neovim を捨てることになります。 Neovim v0.2.3-240-g6ff13d (ほぼ現在の HEAD) で この辺のサポートは結構あとだった気がするので |
|
@lambdalisue うお,マジですか.8f6b866 に強い動機があるわけではないため,削除しようと思います.ご指摘ありがとうございます. |
45cea5d to
337e8c4
Compare
|
8f6b866 を削除するために たくさんのレビューありがとうございます.2時を回ったので,今日はこのあたり( #567 (comment) )にしてまた明日の夜に修正予定です. 目下のところ,議論すべきは |
There was a problem hiding this comment.
[vimlint] reported by reviewdog 🐶
EVL102: unused variable l:is_empty
Discussion point: Error HandlingProblem: call s:P.new({-> execute('throw "oops"')})
\.catch({err -> execute('echo err', '')})We can refer 1. Concat
|
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
この書き方たと、例えば echo "halo" の部分が echon "1halo" だと || の左側が TRUE になるので resolve(42) が実行されません。つまりたまたまうまく動くだけのように見えます…。
そうと知らずにこの例をコピーして使う人が出てくることを危惧しています。
doc/vital/Async/Promise.txt
Outdated
There was a problem hiding this comment.
なるほど。この help 内でも Fulfilled Promise のようなワードがあったので、resolve の方が表記が揺れなくてわかりやすそうという意図なら、これらも Resolved Promise にするのがいいかも?
とは言え私もそこまで強い意志はないです。意図してないうっかりだったらアレなので念のため確認くらいの気持ちでした。
test/Async/Promise.vimspec
Outdated
test/Async/Promise.vimspec
Outdated
There was a problem hiding this comment.
一応 master に入ってる最新版 themis.vim ならいけるはずです(が、別に無理にしなくて OK)。
There was a problem hiding this comment.
おっと,そうですね.rebase して最新の themis を使うようにします.
| End | ||
|
|
||
| It should make settled Promise with v:null when no argument is given to resolve()/reject() | ||
| let l = l: |
There was a problem hiding this comment.
私が言い出しておいてアレですが、この使い方なら無理に l: 使わなくても新規の辞書 {} でも良さそう…。(その場合は Assert の部分が l.result になる)
There was a problem hiding this comment.
分かる… うーん,ただ l. 付けなくて良い利点はあるんですよね.特に理由が無ければこのままでも良いかなぁと
There was a problem hiding this comment.
ハック感が抜ける(…までは行かなくても薄れる)メリットはありそうですが、全部直すの面倒なのも同意なのでこのままでも OK です 🙆
test/Async/Promise.vimspec
Outdated
There was a problem hiding this comment.
.then() と .catch() は Promise オブジェクトのメソッド(モジュール直下の関数ではない)ので、別途 Describe Promise object 的なブロックに入れるのが良さそう。
| End | ||
| End | ||
|
|
||
| Describe .reject() |
There was a problem hiding this comment.
.reject() に resolved Promise を渡した場合のテストがほしいなー。
thinca
left a comment
There was a problem hiding this comment.
間に合わなかった!
些末なコメントをしましたが特筆すべきことは特にありませんでした。
改めて実装ありがとうございます!素晴らしいです 👏
There was a problem hiding this comment.
これわざわざ type() 使ってますが、仕様上 v:null を使うことになっているので foo is v:null なチェックでも良さそうな気がします。そうすれば左辺値に type() を適用する必要がなくなるのでほんの少し速くなるかも。
There was a problem hiding this comment.
確かにわざわざ型の ID を見ているのは冗長ですね.ありがとうございます,直します.
| call s:_fulfill(a:promise, Result) | ||
| elseif a:settled == s:REJECTED | ||
| call s:_reject(a:promise, Result) | ||
| endif |
There was a problem hiding this comment.
💭 [IMO]
ちょっと読んでて混乱しました。
最初の pending の判定を除くと、上2つが has_callback が TRUE の場合、下 2 つが has_callback が FALSE の場合なので、L43 の if の中にそれぞれ書いてしまっても良いかも。
その場合 pending の判定が重複しちゃうのでどちらが良いかむずいですが… 中に入れてしまえば L54 の不要な代入も自然に減らせそうな気がします。
There was a problem hiding this comment.
確かにここはちょっと冗長かつ微妙な実装になっているのですが,意図としてはコールバックの発火処理と次の(子供の)Promise へチェーンする処理を分離する実装になっています.
a:promise._state != s:PENDING をダブらせて上の if の中で判定するというのも考えたのですが,コールバック実行部分とチェーンの続きを発火する部分が混ざると,それはそれで結構読みづらい印象でした.
Discussion for ES6 Promise: promises-aplus/promises-spec#108 Standard ES6 Promise does not check self fulfillment. And in Vim script, there is no way to refer itself in thenable object because Promise object is copy of s:PROMISE.
because it is usually shorter and faster. (As long as I measured, partial is 10% faster than lambda when some variable is captured.
Although previous code has no problem, but linter complains about it.
Thank you @lambdalisue and @tyru!
nit festival! Thank you @haya14busa
23e695d to
fbed16a
Compare
|
themis.vim のアップデートの取り込みと fixup になっている箇所の適用のために push -f しました. @thinca 明らかに直したほうが良さそうなところを修正しました.再度修正箇所とレビューコメントの確認をお願いできますでしょうか |
| End | ||
|
|
||
| It should make settled Promise with v:null when no argument is given to resolve()/reject() | ||
| let l = l: |
There was a problem hiding this comment.
ハック感が抜ける(…までは行かなくても薄れる)メリットはありそうですが、全部直すの面倒なのも同意なのでこのままでも OK です 🙆
|
では,これでマージしようと思います.細かいところまでチェックいただいた皆さんありがとうございました🙏 |
This PR takes over #526 because its timeline is too long.
I rebased the branch into 7 commits. Please see #526 for the detail of design of this library.
As described in #526, this PR only implements APIs of ES6 promises. So discussion for adding other (maybe useful) APIs is out of scope. Let's start from small library. After the smallest set of Promise is added to master branch, we can try it in real Vim plugin. And then, let's discuss further rich APIs (like bluebird).
In this PR, I want to
Thank you for your review in advance.
As of #526, either English or Japanese is ok.