Design: 提案非同期/埅機JS API

䜜成日 2021幎07月26日  Â·  16コメント  Â·  ゜ヌス: WebAssembly/design

この提案は、 @ fmccabe 、 @ thibaudmichaud 、 @ lukewagner 、および@kripkenず

この提案の目的は、JavaScriptのpromiseずWebAssemblyの間に比范的効率的で比范的人間味のある盞互運甚を提䟛するこずですが、倉曎はJS APIのみであり、コアwasmではないずいう制玄の䞋で機胜したす。
スタックスむッチングプロポヌザルは、最終的にコアWebAssemblyを拡匵しお、このプロポヌザルで提䟛する操䜜をWebAssembly内で盎接実装する機胜ず、他の倚くの貎重なスタックスむッチング操䜜を実装するこずが期埅されたすが、
詳现に぀いおは、2021幎6月28日のスタックサブグルヌプ䌚議のメモずスラむドを参照しおください。この

曎新StacksサブグルヌプがTC39から受け取ったフィヌドバックに続いお、この提案ではWebAssemblyスタックの䞀時停止asycn /のサポヌトは間接的に有効になりたせん。 JavaScriptのawait 。

これは、 FunctionサブクラスずしおWebAssembly.Functionを導入するjs-typesの提案に倧たかに䟝存したす。

むンタヌフェヌス

提案は、以䞋のむンタヌフェヌス、コンストラクタヌ、およびメ゜ッドをJS APIに远加するこずであり、それらのセマンティクスの詳现を以䞋に瀺したす。

interface Suspender {
   constructor();
   Function suspendOnReturnedPromise(Function func); // import wrapper
   // overloaded: WebAssembly.Function suspendOnReturnedPromise(WebAssembly.Function func);
   WebAssembly.Function returnPromiseOnSuspend(WebAssembly.Function func); // export wrapper
}

䟋

以䞋は、このAPIの䜿甚方法の䟋です。
私たちの䜿甚シナリオでは、WebAssemblyモゞュヌルが「同期」および「非同期」のむンポヌトず゚クスポヌトを持っおいるず考えられるず䟿利であるこずがわかりたした。
珟圚のJSAPIは、「同期」むンポヌトず゚クスポヌトのみをサポヌトしおいたす。
Suspenderむンタヌフェヌスのメ゜ッドは、「非同期」を䜜成するために関連するむンポヌトず゚クスポヌトをラップするために䜿甚され、Suspenderオブゞェクト自䜓がこれらのむンポヌトず゚クスポヌトを明瀺的に接続しお、実装ず構成の䞡方を容易にしたす。

WebAssembly demo.wasm 

(module
    (import "js" "init_state" (func $init_state (result f64)))
    (import "js" "compute_delta" (func $compute_delta (result f64)))
    (global $state f64)
    (func $init (global.set $state (call $init_state)))
    (start $init)
    (func $get_state (export "get_state") (result f64) (global.get $state))
    (func $update_state (export "update_state") (result f64)
      (global.set (f64.add (global.get $state) (call $compute_delta)))
      (global.get $state)
    )
)

テキスト data.txt 

19827.987

JavaScript

var suspender = new Suspender();
var init_state = () => 2.71;
var compute_delta = () => fetch('data.txt').then(res => res.text()).then(txt => parseFloat(txt));
var importObj = {js: {
    init_state: init_state,
    compute_delta: suspender.suspendOnReturnedPromise(compute_delta)
}};

fetch('demo.wasm').then(response =>
    response.arrayBuffer()
).then(buffer =>
    WebAssembly.instantiate(buffer, importObj)
).then(({module, instance}) => {
    var get_state = instance.exports.get_state;
    var update_state = suspender.returnPromiseOnSuspend(instance.exports.update_state);
    ...
});

この䟋では、非垞に単玔なステヌトマシンであるWebAssemblyモゞュヌルがありたす。状態を曎新するたびに、むンポヌトを呌び出しおデルタを蚈算し、状態に远加したす。
ただし、JavaScript偎では、デルタの蚈算に䜿甚する関数を非同期で実行する必芁があるこずがわかりたした。 ぀たり、数倀自䜓ではなく、数倀の玄束を返したす。

新しいJSAPIを䜿甚するこずで、この同期のギャップを埋めるこずができたす。
この䟋では、WebAssemblyモゞュヌルのむンポヌトはsuspender.suspendOnReturnedPromiseを䜿甚しおラップされ、゚クスポヌトはsuspender.returnPromiseOnSuspendを䜿甚しおラップされ、どちらも同じsuspenderたす。
そのsuspenderは2぀に䞀緒に接続したす。
これにより、ラップされおいないむンポヌトがPromiseを返す堎合、ラップされた゚クスポヌトはPromiseを返し、その間のすべおの蚈算は、むンポヌトのPromiseが解決されるたで「䞀時停止」されたす。
゚クスポヌトのラッピングは基本的にasyncマヌカヌを远加し、むンポヌトのラッピングは基本的にawaitマヌカヌを远加したすが、JavaScriptずは異なり、 asyncを明瀺的にスレッド化する必芁はありたせん。 awaitすべおの䞭間WebAssembly関数を介しお

䞀方、初期化䞭にinit_stateに察しお行われた呌び出しは、必ず䞭断せずに返され、゚クスポヌトget_state呌び出しも垞に䞭断せずに返されるため、プロポヌザルは既存の「同期」むンポヌトおよび゚クスポヌトを匕き続きサポヌトしたす。 WebAssembly゚コシステムは今日䜿甚しおいたす。
もちろん、同期゚クスポヌトが非同期むンポヌトを呌び出す堎合、むンポヌトが䞀時停止しようずするずプログラムがトラップするずいう事実など、倚くの詳现がざっず芋られおいたす。
以䞋に、より詳现な仕様ずいく぀かの実装戊略を瀺したす。

仕様

Suspenderは、次のいずれかの状態にありたす。

  • 非アクティブ-珟圚䜿甚されおいたせん
  • アクティブ[ caller ]-コントロヌルはSuspenderにあり、 callerはSuspenderを呌び出し、 externref期埅しおいる関数です。返される
  • 䞀時停止-珟圚、解決の玄束を埅っおいたす

メ゜ッドsuspender.returnPromiseOnSuspend(func) 、 funcが[ti*] -> [to]の圢匏の関数型のWebAssembly.Functionであるこずを衚明し、関数型のWebAssembly.Functionを返したす。 [ti*] -> [externref]は、匕数args呌び出されるず、次のこずを行いたす。

  1. suspenderの状態が非アクティブでない堎合にトラップしたす
  2. suspenderの状態をアクティブ[ caller ]に倉曎したすここで、 callerは珟圚の呌び出し元です
  3. resultをfunc(args) たたはトラップたたはスロヌされた䟋倖を呌び出した結果ずしたす。
  4. suspenderの状態がいく぀かのcaller'アクティブ[ caller' ]であるこずを衚明したす呌び出し元が倉曎された可胜性がありたすが、保蚌されおいるはずです
  5. suspenderの状態を非アクティブに倉曎したす
  6. resultをcaller'返すたたは再スロヌする

メ゜ッドsuspender.suspendOnReturnedPromise(func)

  • funcがWebAssembly.Function堎合、その関数型は[t*] -> [externref]の圢匏であるず䞻匵し、関数型[t*] -> [externref] WebAssembly.Functionを返したす。 ;
  • それ以倖の堎合は、 funcがFunctionず䞻匵し、 Functionを返したす。

いずれの堎合も、 suspender.suspendOnReturnedPromise(func)によっお返される関数は、匕数argsを指定しお呌び出されるず、次のようになりたす。

  1. resultをfunc(args) たたはトラップたたはスロヌされた䟋倖を呌び出した結果ずしたす。
  2. resultが返されたPromiseでない堎合は、 result返したすたたは再スロヌしたす。
  3. suspenderの状態がアクティブでない堎合にトラップ[ caller ]䞀郚のcaller
  4. caller以降、 framesスタックフレヌムずしたす。
  5. frames䞀時停止䞍可胜な関数のフレヌムがある堎合はトラップしたす
  6. suspenderの状態を䞀時停止に倉曎したす
  7. 次の関数onFulfilledおよびonRejectedを䜿甚しおresult.then(onFulfilled, onRejected)の結果を返したす。

    1. suspenderの状態が䞀時停止されおいるこずを衚明したす保蚌する必芁がありたす

    2. suspenderの状態をアクティブ[ caller' ]に倉曎したす。ここで、 caller'はonFulfilled / onRejectedの呌び出し元です。



      • onFulfilled堎合、指定された倀をexternrefに倉換し、それをframes返したす。


      • onRejected堎合、䟋倖凊理プロポヌザルのJS APIに埓っお、指定された倀を䟋倖ずしおframesたでスロヌしたす。



だった堎合、関数は䞀時停止可胜です

  • WebAssemblyモゞュヌルによっお定矩され、
  • suspendOnReturnedPromiseによっお返される、
  • returnPromiseOnSuspendによっお返される、
  • たたはサスペンド可胜な関数のホスト関数を

重芁なこずには、JavaScriptで蚘述された機胜は、メンバヌからのフィヌドバックに適合し、懞濁ないTC39゚ンゞンの保守からのフィヌドバックに準拠し、䞊蚘の数を陀いお、およびホスト機胜懞濁されおいたせん。

実装

以䞋は、この提案の実装戊略です。
これは、スタックスむッチングの゚ンゞンサポヌトを前提ずしおいたす。もちろん、これが䞻な実装䞊の課題です。

スタックには、ホストおよびJavaScriptスタックずWebAssemblyスタックの2皮類がありたす。 すべおのWebAssemblyスタックには、 suspenderずいうサスペンダヌフィヌルドがありたす。 すべおのスレッドにはホストスタックがありたす。

すべおのSuspenderは、2぀のスタック参照フィヌルドがありたす。1぀はcallerず呌ばれ、もう1぀はsuspendedず呌ばれたす。

  • 非アクティブ状態では、䞡方のフィヌルドがnullです。
  • アクティブ状態では、 callerフィヌルドは呌び出し元の䞀時停止されたスタックを参照し、 suspendedフィヌルドはnullです。
  • Suspended状態では、 suspendedフィヌルドは珟圚サスペンダヌに関連付けられおいるサスペンドされたWebAssemblyスタックを参照し、 callerフィヌルドはnullです。

suspender.returnPromiseOnSuspend(func)(args)はによっお実装されたす

  1. suspender.callerずsuspended.suspendedがnullであるこずを確認したすそれ以倖の堎合はトラップしたす
  2. stackをsuspender関連付けられた新しく割り圓おられたWebAssemblyスタックずしたす
  3. stack切り替えお、前のスタックをsuspender.caller保存したす
  4. resultをfunc(args) たたはトラップたたはスロヌされた䟋倖の結果ずしたす。
  5. suspender.caller切り替えおnullに蚭定
  6. stack解攟する
  7. result返すたたは再スロヌする

suspender.suspendOnReturnedPromise(func)(args)はによっお実装されたす

  1. func(args)呌び出し、トラップたたはスロヌされた䟋倖をキャッチしたす
  2. resultが返されたPromiseでない堎合、 result返すたたは再スロヌする
  3. suspender.callerがnullでないこずを確認したすそれ以倖の堎合はトラップしたす
  4. stack珟圚のスタックずしたす
  5. stackはsuspender関連付けられたWebAssemblyスタックではありたせんが

    • stackがWebAssemblyスタックであるこずを確認したすそれ以倖の堎合はトラップしたす

    • stackをstack.suspender.caller曎新したす

  6. suspender.callerに切り替え、nullに蚭定し、前のスタックをsuspender.suspended栌玍したす
  7. によっお実装された関数onFulfilledおよびonRejectedを䜿甚しおresult.then(onFulfilled, onRejected)の結果を返す

    1. suspender.suspendedに切り替え、nullに蚭定し、前のスタックをsuspender.caller栌玍したす



      • onFulfilled堎合、指定された倀をexternrefに倉換しお返したす


      • onRejected堎合、指定された倀を再スロヌしたす



サスペンド可胜な関数のホスト関数を

党おのコメント16件

非同期関数/ゞェネレヌタヌ同期たたは非同期を受信し、それをサスペンド可胜な関数に倉換するAPIを公開するこずは可胜ですか

おそらく、いく぀かの擬䌌コヌドたたはナヌスケヌスを䜿甚しお、あなたが䜕を意味するのかを明確にできたすか 私はあなたに正確な答えを䞎えるこずを確認したいず思いたす。

SuspenderがJSの䞀郚になるずいう意図ですか、それずも別のAPIですか それはwasm WebAssembly.Suspender 専甚ですか この提案はTC39で議論されるべきだず私には思えたす。

特にJSプログラムに圱響を䞎えるこずを意図したものではありたせん。 より正確には、JS関数を䞀時停止しようずするず、トラップが発生したす。 これを確実にするために、私たちはいく぀かの問題に盎面したした。
しかし、私は圌の意芋を埗るためにシュりナずそれを䞊げるこずができたす。

申し蚳ありたせんが、 @ chicoxyzzy 、Stacksサブグルヌプからのコンテキスト/曎新を含めるのを忘れたようです。 叀いスタック切り替えの提案は、あなたが䞭断し、スタック内のJavaScript /ホストフレヌムをキャプチャするこずができるはずずいう期埅で曞かれおいたした。 ただし、TC39の担圓者からは、JS゚コシステムに倧きな圱響を䞎えるこずが懞念されるずいうフィヌドバックが寄せられ、ホストの実装者からは、すべおのホストフレヌムが停止に耐えられるずは限らないずいうフィヌドバックが寄せられたした。 そのため、Stacksサブグルヌプは、デザむンが䞭断されたスタック内のWebAssembly関連フレヌムのみをキャプチャするこずを保蚌しおおり、この提案はその特性を満たしおいたす。 この重芁なメモを含めるようにOPを曎新したした。

ここで進歩を芋るのは玠晎らしいこずです。 WasmのESM統合でこれがどのように䜿甚されるかの䟋はありたすか

悪いニュヌスは、これはすべおJS APIにあるため、ESM wasmモゞュヌルを単玔にむンポヌトしお、promiseのこのスタックスむッチングサポヌトを取埗するこずはできないずいうこずです。 幞いなこずに、䞀郚のJS ESMモゞュヌルを接着剀ずしお䜿甚するだけで、このAPIでESMモゞュヌルを匕き続き䜿甚できたす。

特に、 foo-exports.js 、 foo-wasm.wasm 、およびfoo-imports.js 3぀のESMモゞュヌルを蚭定したす。 foo-imports.jsモゞュヌルはサスペンダヌを䜜成し、それを䜿甚しおfoo-wasm.wasmに必芁なすべおの「非同期」プロミス生成むンポヌトをラップし、サスペンダヌずそれらのむンポヌトを゚クスポヌトしたす。 次に、 foo-wasm.wasm 、すべおの「非同期」むンポヌトをfoo-imports.jsからむンポヌトし、すべおの「同期」むンポヌトをそれぞれのモゞュヌルから盎接むンポヌトしたすたたは、もちろん、 foo-imports.js介しおプロキシするこずもできたす。 foo-exports.jsはfoo-imports.jsからサスペンダヌをむンポヌトし、 foo-wasm.wasmの゚クスポヌトをむンポヌトし、サスペンダヌを䜿甚しお「非同期」゚クスポヌトをラップしおから、ラップされおいない「同期」を゚クスポヌトしたす。゚クスポヌトずラップされた「非同期」゚クスポヌト。 次に、クラむアントはfoo-exports.jsからむンポヌトし、 foo-wasm.wasmたたはfoo-imports.js盎接觊れるたたは知識が必芁になるこずfoo-exports.jsありたせん。

これは残念なハヌドルですが、コアwasmを倉曎しないずいう制玄を考えるず、達成できる最善の方法でした。 ただし、この蚭蚈が提案拡匵コアwasmず䞊䜍互換性があるこずを確認し、その提案が出荷されたずきに、これら3぀のモゞュヌルを1぀の拡匵wasmモゞュヌルに亀換でき、意味的に誰もできないようにするこずを目指しおいたす。違いを教えおくださいモゞュロファむルの名前倉曎。

それは理解できたしたか、そしおそれはあなたのニヌズに圹立぀ず思いたすか厄介ですが

少なくずもWebAssembly.ModuleタむプのWasmむンポヌトがただ可胜ではない間は、ラッピングの必芁性を理解しおいたすそしお、うたくいけば、やがおむンポヌトされるでしょう。

より具䜓的には、ESM統合でこれらのパタヌンを装食しお、サスペンダヌ接着剀の䞡偎をより管理しやすくする䜙地があるのではないかず考えおいたした。 たずえば、゚クスポヌトされた関数ずむンポヌトされた関数をバむナリ圢匏でリンクするメタデヌタがある堎合、ESM統合はそれを照䌚し、特定の予枬可胜なルヌルに基づいお、統合レむダヌの䞀郚ずしお内郚でデュアルむンポヌト/゚クスポヌトラッピングサスペンダヌ関数を照合できたす。

ああ。 珟圚、そのような蚈画はありたせん。 私が受け取ったフィヌドバックは、ESM統合も倉曎したくないずいう芁望があったずいうものでした。 芁するに、これらすべおが最終的にコアwasmで可胜になるこずを期埅しおいるので、この提案では可胜な限り小さなフットプリントを残しおほしいず考えおいたす。

私が受け取ったフィヌドバックは、ESM統合も倉曎したくないずいう芁望があったずいうものでした。

このフィヌドバックがどこから来おいるのか詳しく説明できたすか ESM統合をより高いレベルの統合セマンティクスで拡匵する䜙地はたくさんありたす。私が感じおいないスペヌスは十分に調査されおいるので、なぜそれを取り䞊げるのですか。 私は過去にこの分野を改善するこずぞの抵抗に぀いお聞いたこずがありたせん。 これを砂糖挬けの領域ず芋なすこずは、JS開発者がPromiseの盎接むンポヌト/゚クスポヌトを蚱可する䞊でメリットになる可胜性がありたす。

この提案は、サむクル内の単䞀のJSモゞュヌルがWasmモゞュヌルのむンポヌタヌずむンポヌティの䞡方になる胜力を劚げるこずは泚目に倀したす。これは、ESM統合でのJSサむクル関数のホむストのおかげで、珟時点では関数のむンポヌトで機胜したす。 、ただし、むンポヌトされた関数の呚りにSuspender匏ラッパヌを䜿甚したこのサむクルホむストはサポヌトされたせん。

@lukewagnerからこの印象を受けたした。 ESM統合を拡匵する䜙地があるこずに同意したすが、これにはフットプリントの小さい目暙の䞀郚ずしお回避しようずしおいたwasmファむルの倉曎/拡匵が必芁であるため、そのような倉曎は必芁ありたせんでした/この提案の䞀郚ずなる拡匵機胜。 もちろん、そのような倉曎/拡匵がESM提案に远加された堎合、これらは理想的にはこの提案を補完するので、この提案が提䟛する機胜を取埗するためにJSラッパヌモゞュヌルは必芁ありたせん。

@ Jack-Worksのコメントを読み間違えたした。䞊蚘のコメントを調敎したしたが、

明確化しおくれた@RossTateに感謝したす。はい、ホスト統合を通知するために、バむナリ自䜓のメタデヌタを介しおこれらのむンポヌトおよび゚クスポヌトの䞀時停止コンテキストに䞀臎する可胜性を探るこずを提案しおいたすが、MVPでは決しおそれを期埅しおいたせん。 たた、ESM統合は、基本のJS APIずは別に、より䞀般的に砂糖の恩恵を受ける可胜性があるスペヌスであるこずを指摘する機䌚を利甚しおいたす。

明確にするために、私が指摘した課題は、 WebAssembly.instantiate() たたは新しいパラメヌタヌを持぀WebAssembly.instantiate()新しいバヌゞョンに远加したオプションも、esmを介しおwasmがロヌドされたずきに䜕らかの圢で衚瀺される必芁があるずいうこずでした-統合、ESM統合ではありたせん-統合は䞍倉でした。

ああ、かっこいいので、必芁が生じた堎合に備えお、ESMに関しお私が思っおいたよりも柔軟性がありたす。 私の誀解を蚂正しおくれおありがずう。

゚クスポヌトされた特定のWasm関数をPromiseベヌスのAPIずしおJSに衚瀺する方法を指定するための、ある皮のカスタムセクションに぀いお話しおいるようです。逆に、WasmからのむンポヌトをJSPromiseベヌスのAPIからある皮に倉換する方法を指定するこずもできたす。スタックスむッチングの。 私は正しく理解しおいたすか

私はこの考えが奜きです。 Wasm GC / JS-ESM統合たたは同じセクションの䞀郚に類䌌したカスタムセクションが必芁になるず思いたす。 このカスタムセクションがどの皋床クロスランゲヌゞであるかはわかりたせんが、どちらの堎合も、おそらくむンタヌフェむスタむプよりも少し普遍的ではなく、コンポヌネント間だけでなくコンポヌネント内でも䜿甚される傟向がありたす。

このカスタムセクションの基本的な蚭蚈を説明するある皮の芁点たたはREADMEを曞きたい人はいたすか

それは可胜な遞択肢のようです。 おっしゃるように、WebAssembly / gc203などのGC提案でも同様のオプションが議論されおいたす。 JS統合は、明日GCサブグルヌプで議論される予定です。そのため、その議論の間、この提案ずの関連の可胜性を念頭に眮いおおくずよいでしょうたたは、議論の進め方によっおは、無関係であるこずが刀明する堎合がありたす。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡

関連する問題

thysultan picture thysultan  Â·  4コメント

spidoche picture spidoche  Â·  4コメント

Artur-A picture Artur-A  Â·  3コメント

ghost picture ghost  Â·  7コメント

badumt55 picture badumt55  Â·  8コメント