Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
spion committed May 24, 2019
0 parents commit 4f0a476
Show file tree
Hide file tree
Showing 24 changed files with 4,044 additions and 0 deletions.
31 changes: 31 additions & 0 deletions 01-the-simplest-example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, target-densityDpi=medium-dpi"/><meta content="True" name="HandheldFriendly"/><title>The simplest example</title><link rel="shortcut icon" href="/ico/favicon.ico"/><link rel="stylesheet" href="/styles/style.css"/><link rel="stylesheet" href="/styles/nuggets.css"/><link rel="stylesheet" href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css"/></head><body><div class="main"><a href="https://github.com/promise-nuggets/promise-nuggets.github.io"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"/></a><h1><a class="back" href="/"></a><span>The simplest example</span></h1><div class="content"><p>Whats the main difference between callback-based functions and promise-based
functions?</p>
<p>The first call the callback with the error and the result:</p>
<pre><code class="language-js">fs.readFile(path, function(error, content) {
// handle error or do something with content
})</code></pre>
<p>The second return promises. We can attach two callbacks - one for the value,
another to handle the error:</p>
<pre><code class="language-js">fs.readFileAsync(path).done(function(content) {
// do something with content
}, function(error) {
// handle error
})</code></pre>
<h2 id="notes">Notes</h2>
<p>Whats going on here?</p>
<p><code>fs.readFileAsync(file)</code> starts a file reading operation.
That operation is not yet complete at the point when readFile returns. This
means we can&#39;t return the file content.</p>
<p>But we can still return something: we can return the reading operation itself.
And that operation is represented by a promise.</p>
<p>It&#39;s is sort of like a single-value stream:</p>
<pre><code class="language-js">net.connect(port).on(&#39;data&#39;, function(res) {
doStuffWith(res);
}).on(&#39;error&#39;, function(err) {
handleError();
});</code></pre>
<p>But there are also some important differences which are going to be covered
later on.</p>
</div></div><script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script><script>function $(q) { return [].slice.call(document.querySelectorAll(q)); }
$('code').forEach(function(el) { el.className += ' prettyprint'; });
prettyPrint();</script></body></html>
14 changes: 14 additions & 0 deletions 02-creating-new-functions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<p>Creating simple functions is easy.</p>
<h4 id="callbacks">Callbacks</h4>
<p>To create a callback-taking function, add a callback argument to your function.
Then you can pass the callback to another callback-taking function.</p>
<pre><code class="language-js">function readMyFile(callback) {
fs.readFile(&#39;myfile.txt&#39;, callback);
}</code></pre>
<h4 id="promises">Promises</h4>
<p>To create a promise-based function, simply return the promise as a result.</p>
<pre><code class="language-js">function readMyFile() {
return fs.readFileAsync(&#39;myfile.txt&#39;);
}</code></pre>
<p>But what if you want to do additional processing? Then you&#39;ll also need to
<a href="03-power-of-then-sync-processing.html">create your own callback, or use the power of <code>.then()</code></a>.</p>
17 changes: 17 additions & 0 deletions 02-creating-new-functions.html.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, target-densityDpi=medium-dpi"/><meta content="True" name="HandheldFriendly"/><title>Creating new functions</title><link rel="shortcut icon" href="/ico/favicon.ico"/><link rel="stylesheet" href="/styles/style.css"/><link rel="stylesheet" href="/styles/nuggets.css"/><link rel="stylesheet" href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css"/></head><body><div class="main"><a href="https://github.com/promise-nuggets/promise-nuggets.github.io"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"/></a><h1><a class="back" href="/"></a><span>Creating new functions</span></h1><div class="content"><p>Creating simple functions is easy.</p>
<h4 id="callbacks">Callbacks</h4>
<p>To create a callback-taking function, add a callback argument to your function.
Then you can pass the callback to another callback-taking function.</p>
<pre><code class="language-js">function readMyFile(callback) {
fs.readFile(&#39;myfile.txt&#39;, callback);
}</code></pre>
<h4 id="promises">Promises</h4>
<p>To create a promise-based function, simply return the promise as a result.</p>
<pre><code class="language-js">function readMyFile() {
return fs.readFileAsync(&#39;myfile.txt&#39;);
}</code></pre>
<p>But what if you want to do additional processing? Then you&#39;ll also need to
<a href="03-power-of-then-sync-processing.html">create your own callback, or use the power of <code>.then()</code></a>.</p>
</div></div><script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script><script>function $(q) { return [].slice.call(document.querySelectorAll(q)); }
$('code').forEach(function(el) { el.className += ' prettyprint'; });
prettyPrint();</script></body></html>
54 changes: 54 additions & 0 deletions 03-power-of-then-sync-processing.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, target-densityDpi=medium-dpi"/><meta content="True" name="HandheldFriendly"/><title>The power of then - sync processing</title><link rel="shortcut icon" href="/ico/favicon.ico"/><link rel="stylesheet" href="/styles/style.css"/><link rel="stylesheet" href="/styles/nuggets.css"/><link rel="stylesheet" href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css"/></head><body><div class="main"><a href="https://github.com/promise-nuggets/promise-nuggets.github.io"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"/></a><h1><a class="back" href="/"></a><span>The power of then - sync processing</span></h1><div class="content"><p>It&#39;s easy to <a href="02-creating-new-functions.html">just pass the callback or return a promise</a>
if we don&#39;t need to do other stuff to the result.</p>
<p>But what if we wanted to do additional processing? What if we want to read a
single line of a file? Then we&#39;ll need to add some code to split the file into
lines and get the specified line. Let&#39;s see how we can do that.</p>
<h4 id="callbacks">Callbacks</h4>
<p>To do this with callbacks, we&#39;ll pass our custom callback that does the
splitting.</p>
<p>The callback bails-out if there was an error reading the file,
otherwise proceeds to split the content and call the original callback with
the line content:</p>
<pre><code class="language-js">function readLine(file, line, callback) {
fs.readFile(file, function process(err, content) {
if (err) return callback(err);
callback(null, content.toString().split(&#39;\n&#39;)[line]);
});
}
readLine(&#39;myfile.txt&#39;, 2, function(err, line) {
// handle error or use line
});</code></pre>
<h4 id="promises">Promises</h4>
<p>To create a promise-based function we can use another promise method called
<code>.then()</code>. It works exactly the same as <code>.done()</code>, but also returns a promise
for the value returned inside its callbacks.</p>
<p>We can simply return the line from inside the first <code>.then</code> callback. We get a
promise for that line outside of the callback (which we return from the
function)</p>
<pre><code class="language-js">function readLine(file, line) {
return fs.readFileAsync(file).then(function(res) {
return res.toString().split(&#39;\n&#39;)[line];
});
}
readLine(file, line).done(function(line) {
// use line
}, function(err) {
// handle error
});</code></pre>
<p>When you call a promise&#39;s <code>.then</code> function, a new promise is created and returned
by <code>.then</code>. It&#39;s a promise to apply all the operations inside the then callback
after the original async operation completes, and return the result.</p>
<h2 id="notes">Notes</h2>
<p>In the callback example, we must explicitly handle the error. Since we can&#39;t
deal with that error there, we must call the passed callback to pass that error.</p>
<p>In the promise example, we can skip the error handling function. If we do that,
the error will automatically propagate with the returned promise for a line.
Yes, that means that you don&#39;t have to write <code>if (err) return callback(err)</code>
anymore - promises do the equivalent of that by default.</p>
<p>Another useful way to think of <code>done</code> vs <code>then</code>: <code>Promise.done()</code> is the
equivalent of <code>Array.forEach</code> while <code>Promise.then()</code> is the equivalent of
<code>Array.map</code>. You use the first when you don&#39;t want to create a new promise, and
the second when you do.</p>
</div></div><script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script><script>function $(q) { return [].slice.call(document.querySelectorAll(q)); }
$('code').forEach(function(el) { el.className += ' prettyprint'; });
prettyPrint();</script></body></html>
37 changes: 37 additions & 0 deletions 04-power-of-then-async-operations.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, target-densityDpi=medium-dpi"/><meta content="True" name="HandheldFriendly"/><title>The power of then - async operations</title><link rel="shortcut icon" href="/ico/favicon.ico"/><link rel="stylesheet" href="/styles/style.css"/><link rel="stylesheet" href="/styles/nuggets.css"/><link rel="stylesheet" href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css"/></head><body><div class="main"><a href="https://github.com/promise-nuggets/promise-nuggets.github.io"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"/></a><h1><a class="back" href="/"></a><span>The power of then - async operations</span></h1><div class="content"><p>In the <a href="03-power-of-then-sync-processing.html">previous example</a> we learned
how to apply sync transformations to the result. But what if we need to do
another async operation instead?</p>
<p>For example, what if we want to fetch the user from a database, then fetch all
of his friends?</p>
<h4 id="callbacks">Callbacks</h4>
<p>With callbacks we can nest the friend fetching operation and pass the original
callback.</p>
<pre><code class="language-js">function getUserFriends(id, callback) {
User.findOne({id: id}, function(err, user) {
if (err) return callback(err);
User.find({id: {$in: user.friends}}, callback);
});
}</code></pre>
<h4 id="promises">Promises</h4>
<p>With promises, we can return the promise for the fetched friends from inside
<code>.then</code></p>
<pre><code class="language-js">function getUserFriends(id) {
return User.findOne({id: id}).then(function(user) {
return User.find({id: {$in: user.friends}});
});
}</code></pre>
<p>And we get a promise for the fetched friends outside of <code>.then</code>.</p>
<h2 id="notes">Notes</h2>
<p>How come this works? If we return a promise from <code>.then()</code> won&#39;t we get a
promise for a promise inside <code>.then()</code>?</p>
<p>The answer is no. When <code>.then()</code> sees that we have returned a promise, it will
also try to &quot;unpack&quot; it to get to an actual value. As long as the unpacking
results with another promise, <code>.then()</code> will continue to unpack them.</p>
<p>In the callback example, we must explicitly handle the error. Since we can&#39;t
deal with that error there, we must call the passed callback to pass that error.</p>
<p>In the promise example, if at any point <code>.then</code> encounters a promise that
resolved with an error, it will stop unpacking and propagate that error through
the returned promise.</p>
</div></div><script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script><script>function $(q) { return [].slice.call(document.querySelectorAll(q)); }
$('code').forEach(function(el) { el.className += ' prettyprint'; });
prettyPrint();</script></body></html>
118 changes: 118 additions & 0 deletions 05-the-first-relief-when-sync-becomes-async.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, target-densityDpi=medium-dpi"/><meta content="True" name="HandheldFriendly"/><title>The first relief - when sync becomes async</title><link rel="shortcut icon" href="/ico/favicon.ico"/><link rel="stylesheet" href="/styles/style.css"/><link rel="stylesheet" href="/styles/nuggets.css"/><link rel="stylesheet" href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css"/></head><body><div class="main"><a href="https://github.com/promise-nuggets/promise-nuggets.github.io"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"/></a><h1><a class="back" href="/"></a><span>The first relief - when sync becomes async</span></h1><div class="content"><p>I hear you. Promise code doesn&#39;t look too different from regular node callbacks
or event emitters, except that you use a second callback for the error. And
you&#39;re still not convinced that there is any benefit to using <code>.then()</code> to
create new promises.</p>
<p>Okay then. Lets take things to the next level.</p>
<p>Using <code>.then()</code>, you can attach the callbacks later if you want.</p>
<p>For example <code>fs.readFileAsync(file)</code> returns a promise. That promise is a value,
so you can put that in a var, or return it from a function:</p>
<pre><code class="language-js">var filePromise = fs.readFileAsync(file);
filePromise.done(function(res) { ... }, function(err) {});</code></pre>
<p>Okay, that&#39;s still not much of an improvement. How about this then? You can
attach more than one callback to a promise if you like:</p>
<pre><code class="language-js">filePromise.done(function(res) { uploadData(url, res); });
filePromise.done(function(res) { saveLocal(url, res); },
function(err), {});
// etc</code></pre>
<p>Hey, this is beginning to look more and more like streams - they too can be
piped to multiple destinations. But unlike streams, you can attach more
callbacks and get the value even <em>after</em> the file reading operation completes.</p>
<p>The promise will cache the value, and call your callback right after the next
tick. The file reading operation is always done only once - no need to repeat
it.</p>
<p>All of this makes it easier when you need to switch code that was previously
sync but had to become async for some reason.</p>
<p>For example: imagine that you need to get some stats about two file versions</p>
<ul>
<li>like number of lines in both versions and the number of lines in their diff:</li>
</ul>
<pre><code class="language-js">var first = files[0], other = files[1];
var diffFirstOther = diff(first, other);

var firstLines = first.split(&#39;\n&#39;).length,
otherLines = other.split(&#39;\n&#39;).length;

var diffLines = diffFirstOther.split(&#39;\n&#39;);

var result = {
first: firstLines,
other: otherLines,
diff: diffLines
};</code></pre>
<p>Later on, you figure out that the <code>diff()</code> function is kind of expensive in
terms of CPU power, so you want it to run in another process. Lets see what
are your options:</p>
<h4 id="callbacks">Callbacks</h4>
<p>You need to change the function <code>diff</code> to take a callback, and you move
the diffLines calculation inside. So far so good:</p>
<pre><code class="language-js">var first = files[0], other = files[1];
diff(first, other, function(err, diffFirstOther) {
var diffLines = diffFirstOther.split(&#39;\n&#39;);
});
</code></pre>
<p>But wait, now the result needs to be inside that callback too.</p>
<pre><code class="language-js">var first = files[0], other = files[1];
diff(first, other, function(err, diffFirstOther) {
var diffLines = diffFirstOther.split(&#39;\n&#39;);
var result = {
first: firstLines,
other: otherLines,
diff: diffLines
};
});
var firstLines = first.split(&#39;\n&#39;).length,
otherLines = other.split(&#39;\n&#39;).length;
</code></pre>
<p>This works but its horribly unintuitive because it looks like you&#39;re using
the variables <code>firstLines</code> and <code>otherLines</code> before they are available. So you
decide its best to move those lines at the top:</p>
<pre><code class="language-js">var first = files[0], other = files[1];
var firstLines = first.split(&#39;\n&#39;).length,
otherLines = other.split(&#39;\n&#39;).length;

diff(first, other, function(err, diffFirstOther) {
var diffLines = diffFirstOther.split(&#39;\n&#39;);
var result = {
first: firstLines,
other: otherLines,
diff: diffLines
};
});</code></pre>
<h4 id="promises">Promises</h4>
<p>With promises our code can stay almost exactly the same. No need to move things
around to make them more intuitive. No need to figure out whether we need to
stuff other lines inside our callback because they have a dependency on the
result of that callback. Instead, we combine:</p>
<ul>
<li>the ability to attach callbacks whenever we want,</li>
<li>the power to create new promises by returning from <code>.then()</code>, and</li>
<li>the use of promise values to replace the original values</li>
</ul>
<pre><code class="language-js">var first = files[0], other = files[1];
var pDiffFirstOther = diff(first, other);

var firstLines = first.split(&#39;\n&#39;).length,
otherLines = other.split(&#39;\n&#39;).length;

var pDiffLines = pDiffFirstOther.then(function(diff) {
return diff.split(&#39;\n&#39;);
});

var pResult = pDiffLines.then(function(diffLines) {
return {
first: firstLines,
other: otherLines,
diff: diffLines
};
});</code></pre>
<p>Everything is exactly the same, except that we now have promises instead of
values.</p>
<p>Whenever we want to do some extra sync (or async) processing on these
promises, we can unpack them with <code>.then()</code> and apply our transformation inside
the callback, then return a new value which will be packed in a new promise.
No need to move code around to make it fit under the same callback.</p>
<p>That&#39;s pretty great, isn&#39;t it? But <a href="06-safety-of-then-exceptions.html">wait &#39;til you see what happens with
errors</a></p>
</div></div><script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script><script>function $(q) { return [].slice.call(document.querySelectorAll(q)); }
$('code').forEach(function(el) { el.className += ' prettyprint'; });
prettyPrint();</script></body></html>
Loading

0 comments on commit 4f0a476

Please sign in to comment.