Skip to content
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

WritableStream Example #532

Open
wants to merge 14 commits into
base: gh-pages
Choose a base branch
from
8 changes: 8 additions & 0 deletions streams/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!-- TODO: Replace PLACEHOLDER with feature name. -->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove these TODOs?

Suggested change
<!-- TODO: Replace PLACEHOLDER with feature name. -->

Stream Samples
===
<!-- TODO: Replace PLACEHOLDER in the path to correspond to the real github.io URL. -->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<!-- TODO: Replace PLACEHOLDER in the path to correspond to the real github.io URL. -->

See https://googlechrome.github.io/samples/streams/index.html for a live demo.

<!-- TODO: Replace PLACEHOLDER with the id from the chromestatus.com URL. -->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<!-- TODO: Replace PLACEHOLDER with the id from the chromestatus.com URL. -->

Learn more at https://www.chromestatus.com/feature/5928498656968704
61 changes: 61 additions & 0 deletions streams/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use strict';

let result;
function getWritableStream(queuingStrategy) {
const decoder = new TextDecoder("utf-8");
result = "";
return new WritableStream({
// Implement the sink
write(chunk) {
return new Promise((resolve, reject) => {
let buffer = new ArrayBuffer(2);
let view = new Uint16Array(buffer);
view[0] = chunk;
let decoded = decoder.decode(view, {stream: true});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these variables be const?

Comment on lines +11 to +14
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let buffer = new ArrayBuffer(2);
let view = new Uint16Array(buffer);
view[0] = chunk;
let decoded = decoder.decode(view, {stream: true});
const buffer = new ArrayBuffer(2);
const view = new Uint16Array(buffer);
view[0] = chunk;
const decoded = decoder.decode(view, {stream: true});

ChromeSamples.log("Chunk decoded: " + decoded);
result += decoded
resolve();
});
},
close() {
result = "[MESSAGE RECEIVED] " + result;
ChromeSamples.log(result);
},
abort(e) {
ChromeSamples.log("[SINK] Error: " + e);
}
}, queuingStrategy);
}

function sendMessage(message) {
// defaultWriter is of type WritableStreamDefaultWriter
const defaultWriter = writable.getWriter();
const encoder = new TextEncoder();
const encoded = encoder.encode(message, {stream: true});
encoded.forEach(chunk => {
defaultWriter.ready
.then(() => {
defaultWriter.write(chunk)
.then(() => ChromeSamples.log("Chunk written to sink. 'defaultWriter.write()' promise resolved."))
.catch(e => ChromeSamples.log("[CHUNK] Error: " + e));
});
})
// Calling ready insures that all chunks are written to the sink before the writer is closed.
defaultWriter.ready
.then(() => {
defaultWriter.close()
.then(() => ChromeSamples.log("All chunks written. 'defaultWriter.close()' promise resolved."))
.catch(e => ChromeSamples.log("[STREAM] Error: " + e));
});
}

let writable;
document.querySelector('#sendMessage').addEventListener('click', function() {
// Clear the output from the previous call to sendMessage().
ChromeSamples.clearLog();
// Streams are only meant be used once. Get a stream for each message.
writable = getWritableStream(new CountQueuingStrategy({highWaterMark: 1}));
let message = document.querySelector('#input');
sendMessage(message.value);
message.value = "";
})
43 changes: 43 additions & 0 deletions streams/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
feature_name: WritableStream
chrome_version: 59
feature_id: 5928498656968704
---

<h3>Background</h3>
<p>This sample illustrates the use of <code><a href="https://streams.spec.whatwg.org/#ws">WritableStream</a></code>, which shows how to break apart a longer piece of data and stream it (send it in chunks) to an output.</p>

<p>WritableStreams objects are designed to only be used once. This example creates a new instance every time "Send message" is clicked. The <code>sendMessage()</code> uses that instance to send every byte of the entered message and closes the instance when it is done (<code>defaultWriter.close()</code>).</p>

<p>The data flow in this example is generally from the text entry field to sink. The details are a bit more complicated. The general flow is this.</p>

<ol>
<li>Create an instance of <code>WritableStream</code>. Pass it a data destination, called a 'sink' in it's constructor as a literal.</li>
<li>Call <code>writableStreamInstance.getWriter()</code> to get a defaul writer.</li>
<li>Iterate the chunks of the message and pass each to the default writer.</li>
<li>Close the default writer.</li>
</ol>

<p>If you're familliar at all with streams you may be wondering how backpressure is implemented using this API, especially since the word 'backpressure' doesn't appear as a member of any of the API's interfaces. Three items combine to create a backpressure signal.</p>

<ul>
<li><em><code>highwatermark</code>:</em> Specifies the maximum amout of data that the <code>WritableStream</code> can handle at once. When creating the <code>WritableStream</code> instance, the second property passed to the constructor is a queuing strategy object. This object's constructor takes an object with a <code>highwatermark</code> property. How <code>highwatermark</code> is specified varies depending on the type of queuing strategy object.</li>
<li><em><code>writer.ready</code>: </em>Returns a promise that resolves whenever the underlying data sink is able to receive more data for writing. Check this before adding new data to the pipe <strong>and</strong> before closing the writer.</li>

<li><em>The promise returned by <code>write()</code>:</em> The sink's <code>write()</code> method must return a promise. This tells <code>writer.ready()</code> when to return its promise.</li>
</ul>

{% capture initial_output_content %}

<form id="sample-form">
<div>
<label for="input">Enter a text message:</label>
<input id="input" type="text">

</div>
</form>
<button id="sendMessage">Send message</button>
{% endcapture %}
{% include output_helper.html initial_output_content=initial_output_content %}

{% include js_snippet.html filename='demo.js' %}