Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Web Streams APIの基本と実践、TypeScriptでの活用法 / TSKaig...
Search
tasshi
May 23, 2025
Technology
5
1.3k
Web Streams APIの基本と実践、TypeScriptでの活用法 / TSKaigi 2025 Web Streams API
TSKaigi 2025 のセッション資料です。
セッション情報はこちら↓
https://2025.tskaigi.org/talks/tasshi
tasshi
May 23, 2025
Tweet
Share
More Decks by tasshi
See All by tasshi
2024年のkintone API振り返りと2025年 / kintone API look back in 2024
tasshi
0
270
Streams APIとTCPフロー制御 / Web Streams API and TCP flow control
tasshi
2
550
GitHub Projectsを自動化するGitHub CLIテクニック / Automate GitHub Projects with GitHub CLI
tasshi
0
610
SDK開発チームのプロダクトオーナーが考えていること / Product management of SDK
tasshi
1
1.4k
Node.js製CLIツールのE2Eテストに取り組んでいます / Working on E2E testing of Node.js CLI tools
tasshi
0
1.4k
モダンな開発環境を用いた カンタン/安全なカスタマイズ開発 / kintone devCamp Boost! 2023
tasshi
0
850
Node Streamでメモリ性能改善、そしてWeb Streams APIへ / Improving memory performance of the CLI tool using Node Stream
tasshi
1
3.4k
グローバルチームことはじめ / Bootstrapping a global team
tasshi
1
3.8k
2年目サイボウズ社員とOSS / OSS development of junior engineer in Cybozu
tasshi
0
810
Other Decks in Technology
See All in Technology
TypeScript をより型安全に扱うプラクティス #TSKaigi #TSKaigi2025_kataritai
bengo4com
0
2.1k
Kubernetesで作るAIプラットフォーム
oracle4engineer
PRO
2
190
20250514_未経験から Fintech実務参画まで。学生エンジニアの挑戦録
hideto1008
0
890
Java 30周年記念! Javaの30年をふりかえる
skrb
4
2.8k
ソフトウェア開発現代史: "LeanとDevOpsの科学"の「科学」とは何か? - DORA Report 10年の変遷を追って - #開発生産性_findy
takabow
1
300
型システムを知りたい人のための型検査器作成入門
mame
13
3.1k
Digitization部 紹介資料
sansan33
PRO
1
4k
フルカイテン株式会社 エンジニア向け採用資料
fullkaiten
0
6.8k
Eight Engineering Unit 紹介資料
sansan33
PRO
0
3.4k
研究開発部メンバーの働き⽅ / Sansan R&D Profile
sansan33
PRO
3
17k
AWS と定理証明 〜ポリシー言語 Cedar 開発の舞台裏〜 #fp_matsuri / FP Matsuri 2025
ytaka23
8
1.5k
AIエージェントのフレームワークを見るときの個人的注目ポイント
os1ma
1
420
Featured
See All Featured
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
RailsConf 2023
tenderlove
30
1.1k
Building Applications with DynamoDB
mza
95
6.4k
It's Worth the Effort
3n
184
28k
Making the Leap to Tech Lead
cromwellryan
134
9.3k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
45
9.6k
Stop Working from a Prison Cell
hatefulcrawdad
269
20k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
357
30k
A Tale of Four Properties
chriscoyier
159
23k
The Pragmatic Product Professional
lauravandoore
35
6.7k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Transcript
Web Streams APIの 基本と実践、 TypeScriptでの活用法 2025.05.24 TSKaigi 2025 @tasshi_me 1
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages はじめに
田代 雅治 (@tasshi_me) • 仕事 • サイボウズ株式会社 • kintone開発 拡張基盤 チーム EM • DX (Developer eXperience) デザイン • 週4勤務、週1で副業 • 主にnpmパッケージとサーバーサイドJS • (画面はあまり触らない) • 去年から会社の同期とバンド始めました 自己紹介 2
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages はじめに
• kintone ≒ かんたんに業務アプリを構築できるクラウドサービス • kintoneのプラグイン・連携サービス向けの開発基盤を開発・保守するチーム • API開発、SDK/CLIなどのOSS提供 • npmパッケージを多数メンテナンス kintone開発 拡張基盤 チーム 3
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages はじめに
Streams APIを触ったことがないという声を聞くので、ざっくりStreams APIの説明をします。 また、Node Streamとの違いや、Promiseベースの非同期処理との相互運用性についても話します。 Streams APIを試すきっかけとなると良いかなと思います。 そして、今後の実装の選択肢にStreams APIが追加されたらとても嬉しいです。 (TSKaigiですが、型の話は少ないかも) このセッションでは 4
ストリームとは何か 5 ※様々な技術領域・開発言語でストリームの定義があるため ここでは概ね共通と思われる性質について話します
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages ストリームとは何か
• データ入出力を逐次的に、効率よく扱うためのデータ構造 • データをより細かい分割された単位の連続した流れとして表現する • 昔からある概念 • 多くの開発言語でストリーム操作のインターフェースは提供されている • 古くはUNIXの標準ストリーム、pipe、redirectとか ストリームとは何か 6 変換処理A chunk chunk chunk 変換処理B chunk chunk chunk 変換処理C chunk chunk chunk chunk
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages ストリームとは何か
標準ストリームとパイプの例 7 cat input .txt sed “foo” tee Terminal output .txt “bar” “bar” “bar” “foo” stdout stdin stdin stdout stdout
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages ストリームとは何か
• メモリ空間を圧迫しにくい • データを処理する分だけメモリ上に展開するため • 大規模データや時間経過で無限に増大するデータの処理に有効 • 低遅延 • 先頭のデータが処理されるまでの時間が速い • ※最終的にデータ全体が処理されるまでの時間が早くなるとは限らない ストリームのメリット 8 処理A 処理B ストリーム処理の場合 バッチ処理の場合 時間経過 時間経過
Web Streams API 9 WHATWGのStreams API (https://streams.spec.whatwg.org/)
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Streams
API JavaScriptでストリーム処理を行うためのAPI • データは分割された断片(chunk)の連続した流れとして扱われる • 3種類の役割の異なるストリームオブジェクト • ストリームオブジェクト同士をパイプ接続(パイプチェーン)することで、 chunkは流れるように終端まで処理される Streams APIの概要 10 Transform Stream Readable Stream Writable Stream chunk データの読み込み データの変換 (読み込み+書き込み) データの書き込み chunk Source Sink
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Streams
API 具体例: WebリソースをFetchしてファイルに保存 11 Text Decoder Stream Response. body FileSystem Writable FileStream string Uint8 Array example .com File System
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Streams
API 基となるソース(underlying source)から流れるデータを表現するオブジェクト ソースから流れるデータをチャンクに分割し、ストリーム処理できる形で提供する • 基となるソース:ファイルシステム、ネットワークリソース、メモリ上の配列、など • Pull型/Push型のソースがある(付録を参照) • ReadableStreamの例:Fetch APIのResponse.body、Blob.stream() ReadableStream (読み取り可能なストリーム) 12 Readable Stream chunk Source chunk 読み出し raw data
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Streams
API ストリームに流れるデータをある形式から別の形式に変換するオブジェクト • TransformStreamの例 • TextEncoderStream / TextDecoderStream: バイナリ 文字列の変換 • CompressionStream / DecompressionStream: データの圧縮・展開 (gzip, deflate) TransformStream (変換ストリーム) 13 chunk (string) chunk (Uint8Array) chunk (Uint8Array) TransformStream chunk (string)
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Streams
API 基となるシンク(underlying sink)に流れるデータを表現するオブジェクト • 基となるシンク:ファイルシステム、データベース、など • WritableStreamの例:File System Access APIのFileSystemWritableFileStream WritableStream (書き込み可能なストリーム) 14 Writable Stream chunk Sink chunk 書き込み data
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Streams
API • ReadableStream、TransformStream、 WritableStreamを継承して独自のストリームを作成する • データ処理用のいくつかのメソッドを実装する(pull, write, transform, etc.) • チャンクの型はGenericsで指定 • 後述のキューイング戦略もコンストラクタで指定 • DBアクセスとか、データエンコーディングとか、独自の処理を実装できる 独自のストリームオブジェクトを作成する 15
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Streams
API • Stream同士をパイプ接続すると、chunkは流れるように終端まで処理される(パイプチェーン) • パイプ接続メソッド • Readable.pipeTo(): 終端のWritableStreamに接続 • Readable.pipeThrough(): 中間のTransformStreamに接続 パイプチェーン 16 Transform Stream Readable Stream Writable Stream chunk chunk Source Sink pipeThrough() pipeTo()
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Streams
API • 1つのReadableStreamを2つのReadableStream(branch)に分配 • 分配したストリームはそれぞれ異なる速度で読み取ることができる • FetchしたデータをUIとキャッシュの両方に出力したりできる • 注:内部キューにチャンクが滞留するため、長すぎるデータストリームには適さない(付録を参照) ストリームの分配 (tee) 17 Readable Stream 元のストリーム Readable Stream 分配後のストリーム1 Source Readable Stream 分配後のストリーム2
利用イメージ 18 1. 他サービスからのデータインポート 2. GPTの回答をリアルタイム表示
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 利用イメージ
• メモリ負荷を軽減できる例 • 他サービスからAPI経由で全レコードを取得し、加工して自サービスのDBに書き込んでいく • APIのレスポンスはJSONL形式 1. 他サービスからのデータインポート 19 Text Decoder Stream Response JSON Parser Stream Line Splitter Stream 行ごとに 再分割 (Transform) UTF-8 デコード (Tranform) JSONから オブジェクトに変換 (Transform) レスポンスの 読み込み (Readable) Writable Stream DBに 書き込み (Writable) fetch DB
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 利用イメージ
• リアルタイム性を実現できる例 • API経由でGPTの回答を取得し、画面に表示する • デフォルトの一括応答では、回答全体が生成されてからレスポンスが返却される • ストリーミング応答を有効化すると、回答が生成された分ずつ返却されるようになる 2. GPTの回答をリアルタイム表示 20
内部キューと背圧 21 パイプチェーンの処理速度を調整する仕組みについて
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 内部キューと背圧
• ストリーム(オブジェクト)は未処理チャンクを保持する内部キューを持っている • パイプチェーン上のストリーム間で処理速度に差がある場合、 内部キューにチャンクが溜まっていくことになる => メモリを圧迫する? ストリームオブジェクト間の処理速度の違いとメモリ使用量 22 Transform Stream Readable Stream Writable Stream chunk chunk チャンクが溜まっていく? ここの処理が遅い場合 (DBアクセスなど) ※TransformStream は 書込側・読出側それぞれに 内部キューを持つが省略
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 内部キューと背圧
• チャンクを受け入れられない場合、ストリームは上流に停止信号を出す • 停止信号を受けた上流のストリームはデータの送信を停止する • 下流のストリームが送信を指示(pull)すると再び処理が再開する => チャンクの流速が調整されてメモリを圧迫せずに処理できる 背圧 (Backpressure) 23 Transform Stream Readable Stream Writable Stream chunk chunk 内部キューが満杯 STOP! 内部キューが満杯 STOP!
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 内部キューと背圧
• 背圧は内部キューの状態から、キューイング戦略に基づいて通知される • 現在は2種類のキューイング戦略が利用可能 キューイング戦略と最高水準点(highWaterMark) 24 キューに格納されたチャンク数で判定 キューに格納されたバイト数で判定 chunk chunk chunk chunk chunk chunk chunk chunk 3 最高水準点 (highWaterMark) 10KB CountQueuingStrategy ByteLengthQueuingStrategy
Node.jsのStreamとの違いと互換性 25 https://nodejs.org/api/stream.html
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Node.jsのStreamとの違いと互換性
• Node.jsの組み込みモジュール • こちらのほうが先発 • 「Streamを制するものはNode.jsを制す」と言われていたらしい • EventEmitterを継承していて、イベント駆動的 • highWaterMarkの考え方はNode StreamからWeb Streams APIに影響してそう • 3(+2)種類のストリームオブジェクト Node.js Streamについて 26 Readable 読み込み Duplex 双方向 (読み込み+書き込み) Transform 変換 Writable 書き込み PassThrou gh パススルー (何もしない)
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Node.jsのStreamとの違いと互換性
• Node.jsでもv21でWeb Streams APIがStableになった • Node Stream Web Streams API は toWeb() / fromWeb() メソッドで相互に変換可能 • v17で追加されてから長らくExperimentalだったが、v24でとうとうStableになった Node SteamとWeb Streams APIとの互換性 27
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Node.jsのStreamとの違いと互換性
• Web標準であること • クロスブラウザに利用できる • Fetch APIを始めとして、他の標準仕様にもWeb Streams APIベースのAPIが増えていっている • WinterTC Minimum Common APIにも含まれている • Node.js含めてサーバーサイドJSでも利用できる • インターフェースの改善 • EventEmitter/Callbackな書き方からPromise/async/awaitな書き方に • 利用者に公開されているメソッド・プロパティがかなり減っているため学習コストが減った • (逆に細かい制御がしづらくなったとも) • 型情報 (@types/node) • Node Stream: chunkの型がany • Web Streams API: chunkの型がGenericsで指定できる Node Streamと比べて良くなったと思うところ (1) 28
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Node.jsのStreamとの違いと互換性
• エラーハンドリング • Node Streamでは、上流のストリームがエラー終了しても下流のストリームが閉じない • errorイベントのイベントリスナーで明示的に閉じる必要がある • Web Streams APIでは、勝手に閉じる • pipeThrough()/pipeTo()のpreventAbortオプションで制御可能 Node Streamと比べて良くなったと思うところ (2) 29
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Node.jsのStreamとの違いと互換性
• 変換ストリームの結合が比較的やりやすくなった • Node Stream: Duplexでラップするが、実装が複雑になる • イベントリスナーやメソッドの繋ぎ込みがかなり面倒 • stream.compose()を利用すると簡単に結合できるが、まだExperimental • Web Streams API: TransformStreamでラップしてパイプ接続したらOK Node Streamと比べて良くなったと思うところ (3) 30
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Node.jsのStreamとの違いと互換性
• これから書くコードは最初からWeb Streams APIで良い • Web標準かつPromiseベースの書き方ができる • Node Streamとも toWeb() / fromWeb() で相互変換できる • 昔からあるnpmパッケージはNode Streamを使っている • 当面は toWeb() / fromWeb() メソッドで変換しながら併用することになる Node SteamとWeb Streams API、どっちを使えばいい? 31
Promiseベースの非同期処理との相互運用性 32 • ストリームとPromise • ストリームと反復処理
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Promiseベースの非同期処理との相互運用性
• pipeTo()の返り値がPromise • パイプチェーンでのデータ処理が終わるまでawaitで待つことができる • チャンクを1つずつ操作したい場合 • ReadableStream.getReader()やWritableStream.getWriter()でreader/writerを取得 • await reader.read() / await writer.write() でチャンクを1つずつ読み込み/書き込みできる ストリームをasync/awaitの中で使う 33
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Promiseベースの非同期処理との相互運用性
• ReadableStreamは非同期反復可能([Symbol.asyncIterator]()を実装している)(Safari除く) • for await ... ofで反復処理できる • Array.fromAsync()でデータを全て読み出して配列に格納できる ストリームを反復処理する 34
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages Promiseベースの非同期処理との相互運用性
• ReadableStream.from() • 反復可能オブジェクト or 非同期反復可能オブジェクトからReadableStreamを作成できる • ただしまだFireFox, Deno, Node.jsでしか利用できない イテラブルからストリームを作成する 35 https://developer.mozilla.org/ja/docs/Web/API/ReadableStream/from_static
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages おわりに
• Streams APIには3つのストリームオブジェクトがある • ReadableStream, TransformStream, WritableStream • ストリームオブジェクトをパイプ接続してデータを逐次処理できる • 背圧を制御することで処理速度を制御し、メモリ使用量を適切に抑えることができる • Node Streamとは相互に変換可能 • 新規に書くコードはWeb Streams APIで良い • async/awaitな処理とも組み合わせやすい まとめ 36
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages おわりに
• kintoneを開発する仲間を探しています!! • 採用フォームからでも、XのDMでもどうぞ! • https://cybozu.co.jp/recruit/ We are hiring!! 37 Webエンジニア (kintone) エンジニアリング マネージャー (kintone) Webエンジニア (kintone/生成AI) フロントエンド エキスパート
ご清聴ありがとうございました 38 Ask The Speakerやります! お気軽にご質問ください! 場所:3F 企画AREA 時間:13:50-14:00
付録 39 • ストリームオブジェクトの補足 • ストリームの分配 (tee) と背圧制御の問題 • ネットワーク通信に背圧は反映されているのか?
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 付録:ストリームオブジェクトの補足
• チャンクの読み込みはReader経由で行う • ソースにはPull型とPush型がある • Pull型:データはストリームから明示的に読み込む • 例:ファイルアクセスなど • https://streams.spec.whatwg.org/#example-rs-pull • Push型:データは勝手にソースから送信される、イベントリスナなどでストリームにenqueueする • 例:動画ストリーム、TCP/WebSocketsなど • https://streams.spec.whatwg.org/#example-rs-push-backpressure ReadableStream 40 Source chunk enqueue ReadableStream chunk chunk chunk chunk Reader 内部キュー raw data
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 付録:ストリームオブジェクトの補足
• チャンクの書き込みはWriter経由で行う WritableStream 41 Sink chunk enqueue WritableStream chunk chunk chunk chunk Writer 内部キュー data
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 付録:ストリームオブジェクトの補足
• 内部的にはWritableStream + ReadableStream • 入力側: TransformStream.writable (WritableStream) • 出力側: TransformStream.readable (ReadableStream) • 内部キューも入力側・出力側それぞれにある TransformStream (変換ストリーム) 42 readable chunk chunk writable chunk TransformStream transform() chunk データの入力側 データの出力側 データの変換
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 付録
• tee()で分配された2つのReadableStreamは消費速度が速い方の速度で背圧制御される • 時間経過と共に速度が遅い方の内部キューにデータが滞留してしまう • 背圧制御を変更するオプションが提案されている(whatwg/streams#1235) • Cloudflare Workersでは tee の背圧制御を独自に修正している(cloudflare/workerd#85) • https://community.cloudflare.com/t/467416 ストリームの分配 (tee) と背圧制御の問題 43 Readable Stream Readable Stream 両方に同時にchunkを送信 後続の 処理が速い 後続の 処理が遅い ここにチャンクが溜まってしまう
Web Streams APIの基本と実践、TypeScriptでの活用法 – TSKaigi 2025 #TSKaigi #TSKaigi2025 #tskaigi_leverages 付録
• 検証時の通信方式 • APIレスポンスはJSONL形式 (Content-Type: application/jsonl; charset=utf-8) • HTTP/1.1 • Transfer-Encoding: chunked (レスポンスはチャンク単位で送られてくる) • => 背圧はunderlying sourceへのネットワークリクエストにも反映される • 内部キューが溜まってくるとウィンドウサイズが小さくなり、受信可能データを調整する • それでも処理しきれずに受信を止める場合は、zero windowパケットが送信される ネットワーク通信に背圧は反映されているのか? 44 chunk Server chunk chunk Response STOP! STOP! (zero window) https://speakerdeck.com/tasshi/web-streams-api-and-tcp-flow-control