非同期ストリーム入出力

Curl® 言語 API は、データ ストリームの非同期読み取りをサポートします。非同期アクセスにより、バックグラウンドでデータを読み取りながらアプレットを起動したり実行し続けたりすることができます。これはまた、アプレットが開いてデータ ストリームを読み取っているときにユーザーがアプレットと対話する必要がある場合にも役立ちます。この手法により、ユーザーは、時間がかかりすぎている場合にストリーム操作を取り消し、待機中にアプレットを使用した他のアクションを実行することができます。
非同期入出力が役立つのは、応答が遅かったり入手できなかったりすることがあるインターネット ソースからのデータにアクセスする場合や、大量のデータにアクセスする場合などです。

テキスト ストリームの読み取り

以下のプロシージャは、Url から非同期読み取りを行うものです。
これらのプロシージャは、1 つ以上の EventHandler を引数として受け取ります。これらのイベント ハンドラは AsyncStreamReadEvent を受け取る必要があります。AsyncStreamReadEvent は、ストリームで読み取られたデータを、例外および読み取り要求の状況に関する他の情報とともに返します。これらのイベント ハンドラでデータの初期処理を行います。
次の例では、async-read-from を使用して、テキスト ファイルから非同期に読み取ります。エラー処理を行い、ストリーム データを buffer に書き込むイベント ハンドラを使用して作業することができます。
また、cancel-button はメソッド AsyncStreamReader.cancel を使用して、読み取り操作を取り消します。
この例ではキーワード partial?true に設定するので、文字列が読み取られるごとに AsyncStreamReadEvent が生成されます。読み取り操作を取り消すと、それまでに読み取られたすべての文字列が表示されます。partial? の値を false に変更して、例を実行してみてください。読み取りを取り消すと、buffer には何も現れません。これは、ハンドラの取得する最初の AsyncStreamReadEventAsyncStreamReader.cancel によって生成されるものだからです。
テキストの読み取りやデータベースからのデータ レコードの取得など、いくつかの場合には、読み取り処理全体が完了する前に、partial? = true を使用してデータの表示を開始したいことがあります。
注意: 以下の例では、読み取り操作に character-encoding = "shift-jis" が使用されています。 ユーザーが選択したファイルとこの文字エンコーディング間に互換性がない場合はエラーがスローされます。

例: async-read-from の使用
{paragraph
    Please click the button below and select a text file to read
    in. There is a text file located at
    {{url "curl://install/docs/default/support/example.txt"}.canonicalize}
    if you cannot find one.
}
{let buffer:TextFlowBox =
    {TextFlowBox width = 5in, border-width = 1pt,
        border-color = "black", margin = 2pt
    }
}
{let rdr:#AsyncStreamReader}
{let cancel-button:CommandButton =
    {CommandButton
        label = "Cancel reading",
        {on Action do
            {if-non-null rdr then
                {rdr.cancel}
            }
        }
    }
}
{set cancel-button.enabled? = false}
{VBox
    {CommandButton
        label = "Select File to Read",
        {on Action do
            {buffer.clear} 
            let file-url:#Url = {choose-location}
            {if-non-null file-url then
                set rdr =
                    {async-read-from
                        file-url,
                        || partial? = true means that this will be called
                        || for each chunk of data that is downloaded.
                        partial? = true,
                        character-encoding = "shift-jis",
                        {on asre:AsyncStreamReadEvent do
                            {if-non-null e = asre.exception then
                                {buffer.add
                                    ""
                                }
                             else
                                let buf:#StringBuf =
                                    (asre.data asa #StringBuf)
                                {if-non-null buf then
                                    {buffer.add buf}
                                }
                                {if asre.canceled? then
                                    {buffer.add ""}
                                }
                            }
                            {if asre.exception != null or asre.canceled? or
                                asre.done?
                             then
                                set cancel-button.enabled? = false
                            }
                        }
                    }
                set cancel-button.enabled? = true
            }
        }
    },
    cancel-button,
    buffer
}

テキスト ストリームを開いて読み取る

以下のプロシージャは、Url からのストリームを非同期で開くものです。
これらのプロシージャは、1 つ以上の EventHandler を引数として受け取ります。これらのイベント ハンドラは AsyncFileOpenEvent を受け取る必要があります。AsyncFileOpenEvent はストリームを含みますが、開けなかった場合は例外を含みます。これらのイベント ハンドラで、ストリームに対してアクション (ストリームからの読み取りなど) を実行します。これらのプロシージャは、ストリームへの直接アクセスが必要な場合に使用する必要があります。また、ストリームを閉じる必要もあります。
async-read-open プロシージャは、テキストを文字列として読み取りたくない場合や、ファイルを開く必要はあるが、他のイベントが発生するか、ユーザーからの追加入力があるまでファイルを読み取りたくない場合に役立ちます。また、直ちにストリームの一部を読み取り、後でストリームからの追加読み取りを実行する必要がある場合にも、このプロシージャを使用しなければなりません。async-read-from を使用してストリームの一部を読み取ることができますが、その読み取りが完了すると、ストリームは閉じます。
HttpFile API には読み取り元のバリエーションがないので、ストリームを開くときに HTTP 固有の引数を提供する必要がある場合は、まず HttpFile.http-async-read-open を使用し、次いで TextInputStream.async-read-string または InputStream-of.async-read を使用する必要があります。
次の例では、async-read-open を使用してテキスト ファイルを開き、TextInputStream.async-read-string を使用してそのテキスト ファイルから文字列を読み取ります。この例では async-read-string パラメータ partial?true に設定するので、status-buffer は各データ チャンクごとにメッセージを表示します。また、append?true に設定します。これは、各 AsyncStreamReadEvent がそれまでに読み取られたすべてのデータを返すことを意味します。
注意: 以下の例では、読み取り操作に character-encoding = "shift-jis" が使用されています。 ユーザーが選択したファイルとこの文字エンコーディング間に互換性がない場合はエラーがスローされます。

例: async-read-open の使用
{paragraph
    Please click the button below and select a text file for
    asynchronous read. There is a text file located at
    {{url "curl://install/docs/default/support/example.txt"}.canonicalize}
    if you cannot find one.
}
{let status-buffer:TextFlowBox =
    {TextFlowBox width = 5in, border-width = 1pt,
        border-color = "black", margin = 2pt
    }
}
{let buffer:TextFlowBox =
    {TextFlowBox width = 5in, border-width = 1pt,
        border-color = "black", margin = 2pt
    }
}
{define-proc public {handle-successful-open-event
                        afoe:AsyncFileOpenEvent
                    }:AsyncStreamReader
    let str:TextInputStream =
        (afoe.stream asa TextInputStream)
    {status-buffer.add
        {text
            
        }
    }
    let rdr:AsyncStreamReader =
        {str.async-read-string
            || we will get called multiple times
            || as data comes in.
            partial? = true,
            || The data that we get will be
            || appended to, and so will have all
            || of the data so far.
            append? = true,
            {on asre:AsyncStreamReadEvent do
                {if-non-null e = asre.exception then
                    {status-buffer.add
                        {paragraph
                            color = "red",
                            
                        }
                    }
                 else
                    let buf:#StringBuf =
                        (asre.data asa #StringBuf)
                    {if-non-null buf then
                        {status-buffer.add
                            {paragraph
                                Read has seen
                                {value buf.size} chars.
                            }
                        }
                        {buffer.clear}
                        {buffer.add buf}
                    }
                    {if asre.canceled? then
                        {status-buffer.add
                            {paragraph
                                color = "red",
                                
                            }
                        }
                     elseif asre.done? then
                        {status-buffer.add
                            {paragraph Read has completed.}
                        }
                    }
                }
                {if (asre.exception != null or asre.done?) and str.open? then
                    {str.close}
                }
            }
        }
    {return rdr}
}
{VBox
    {CommandButton
        label = "Select File to Read",
        {on Action do
            {buffer.clear} 
            {status-buffer.clear} 
            let file-url:#Url = {choose-location}
            {if-non-null file-url then
                let opr:AsyncFileOpener =
                    {async-read-open 
                        file-url,
                        character-encoding = "shift-jis",
                        {on afoe:AsyncFileOpenEvent do
                            {if-non-null e = afoe.exception then
                                {status-buffer.add
                                    {paragraph
                                        color = "red",
                                        
                                    }
                                }
                             elseif afoe.canceled? then
                                {status-buffer.add 
                                    {paragraph
                                        color = "red",
                                        
                                    }
                                }
                             else
                                let rdr:AsyncStreamReader =
                                    {handle-successful-open-event afoe}
                                || rdr could be used for canceling.
                            }
                        }
                    }
            }
        }
    },
    status-buffer,
    buffer
}

バイナリ ストリームを開いて読み取る

次の例では、バイナリ データを開いて読み取るための非同期プロシージャを使用します。これらのプロシージャはテキストの場合と非常によく似ています。例では InputStream-of.async-read を使用してファイルを読み取ることに、注意してください。

例: バイナリ データの読み取り
{let results:TextFlowBox = {TextFlowBox}}
{let image-file-types:{Array-of FileDialogFilter} =
    {new
        {Array-of FileDialogFilter},
        {FileDialogFilter
            "Image files",
            {new
                {Array-of FileDialogTypeFilter},
                {FileDialogTypeFilter "bmp"},
                {FileDialogTypeFilter "jpg"},
                {FileDialogTypeFilter "jpeg"},
                {FileDialogTypeFilter "gif"},
                {FileDialogTypeFilter "png"}
            }
        }
    }
}
{paragraph
    Click the button below to choose an image file from
    your system. If you do not have one handy, there is
    a {monospace .gif} file located at
    {value {url "../../default/images/url-accessor-diagram.gif"}.name}
}
{VBox
    {CommandButton
        label="Choose an Image File",
        {on Action do
            {results.clear}
            let file-url:#Url = {choose-file filters = image-file-types}
            {if-non-null file-url then
                let opr:AsyncFileOpener =
                    {async-read-open-byte
                        file-url,
                        {on afoe:AsyncFileOpenEvent do
                            {if-non-null e = afoe.exception then
                                {popup-message "Open failed: " & e}
                             elseif afoe.canceled? then
                                {popup-message "Open was canceled."}
                             else
                                || open succeeded
                                let my-input:ByteInputStream =
                                    (afoe.stream asa ByteInputStream)
                                let rdr:AsyncStreamReader =
                                    {my-input.async-read
                                        {on asre:AsyncStreamReadEvent do
                                            {if-non-null e = asre.exception then
                                                {popup-message
                                                    "Read failed: " & e
                                                }
                                             else
                                                let data:#{Array-of byte} =
                                                    (asre.data asa
                                                     #{Array-of byte})
                                                {if-non-null data then
                                                    {results.add
                                                        {text
                                                            Got {value data.size} 
                                                            bytes of data from the
                                                            file.
                                                        }
                                                    }
                                                }
                                                {if asre.canceled? then
                                                    {popup-message
                                                        "Read was canceled."
                                                    }
                                                }
                                            }
                                            || close the stream since we are
                                            || done with it.
                                            {my-input.close}
                                        }
                                    }
                            }
                        }
                    }
                || you could save opr and rdr so that you can call cancel
                || on either of them if you wanted to cancel a request due
                || to the user requesting it or it taking too long.
            }
        }
    },
    results
}