src/cm/media/js/lib/yui/yui_3.10.3/docs/promise/subclass-example.html
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     1 <!DOCTYPE html>
       
     2 <html lang="en">
       
     3 <head>
       
     4     <meta charset="utf-8">
       
     5     <title>Example: Subclassing Y.Promise</title>
       
     6     <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic">
       
     7     <link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css">
       
     8     <link rel="stylesheet" href="../assets/css/main.css">
       
     9     <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
       
    10     <link rel="shortcut icon" type="image/png" href="../assets/favicon.png">
       
    11     <script src="../../build/yui/yui-min.js"></script>
       
    12     
       
    13 </head>
       
    14 <body>
       
    15 <!--
       
    16 <a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
       
    17 -->
       
    18 <div id="doc">
       
    19     <div id="hd">
       
    20         <h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1>
       
    21     </div>
       
    22     
       
    23         <a href="#toc" class="jump">Jump to Table of Contents</a>
       
    24     
       
    25 
       
    26             <h1>Example: Subclassing Y.Promise</h1>
       
    27     <div class="yui3-g">
       
    28         <div class="yui3-u-3-4">
       
    29             <div id="main">
       
    30                 <div class="content"><style scoped>
       
    31     .error {
       
    32         background: #ffc5c4;
       
    33     }
       
    34 </style>
       
    35 
       
    36 
       
    37 <div class="intro">
       
    38     <p>
       
    39         This example expands on the <a href="basic-example.html">Wrapping async transactions with promises</a> example to illustrate how to create your own Promise subclass for performing operations on arrays.
       
    40     </p>
       
    41 </div>
       
    42 
       
    43 <div class="example yui3-skin-sam">
       
    44     <div id="demo"></div>
       
    45     <script>
       
    46 YUI().use('promise', 'jsonp', 'node', 'array-extras', function (Y) {
       
    47 
       
    48 function ArrayPromise() {
       
    49     ArrayPromise.superclass.constructor.apply(this, arguments);
       
    50 }
       
    51 Y.extend(ArrayPromise, Y.Promise);
       
    52 
       
    53 // Although Y.Array.each does not return an array, for the purpose of this
       
    54 // example we make it chainable by returning the same array
       
    55 ArrayPromise.prototype.each = function (fn, thisObj) {
       
    56     return this.then(function (array) {
       
    57         Y.Array.each(array, fn, thisObj);
       
    58         return array;
       
    59     });
       
    60 };
       
    61 
       
    62 // Y.Array.map returns a new array, so we return the result of this.then()
       
    63 ArrayPromise.prototype.map = function (fn, thisObj) {
       
    64     return this.then(function (array) {
       
    65         // By returning the result of Y.Array.map we are returning a new promise
       
    66         // representing the new array
       
    67         return Y.Array.map(array, fn, thisObj);
       
    68     });
       
    69 };
       
    70 
       
    71 // Y.Array.filter follows the same pattern as Y.Array.map
       
    72 ArrayPromise.prototype.filter = function (fn, thisObj) {
       
    73     return this.then(function (array) {
       
    74         return Y.Array.filter(array, fn, thisObj);
       
    75     });
       
    76 };
       
    77 
       
    78 // Takes any promise and returns an ArrayPromise
       
    79 function toArrayPromise(promise) {
       
    80     return new ArrayPromise(function (fulfill, reject) {
       
    81         promise.then(fulfill, reject);
       
    82     });
       
    83 }
       
    84 
       
    85 
       
    86 // A cache for GitHub user data
       
    87 var GitHub = (function () {
       
    88 
       
    89     var cache = {},
       
    90         githubURL = 'https://api.github.com/users/{user}?callback={callback}';
       
    91 
       
    92     function getUserURL(name) {
       
    93         return Y.Lang.sub(githubURL, {
       
    94             user: name
       
    95         });
       
    96     }
       
    97 
       
    98     // Fetches a URL, stores a promise in the cache and returns it
       
    99     function fetch(url) {
       
   100         var promise = new Y.Promise(function (fulfill, reject) {
       
   101             Y.jsonp(url, function (res) {
       
   102                 var meta = res.meta,
       
   103                     data = res.data;
       
   104 
       
   105                 // Check for a successful response, otherwise reject the
       
   106                 // promise with the message returned by the GitHub API.
       
   107                 if (meta.status >= 200 && meta.status < 300) {
       
   108                     fulfill(data);
       
   109                 } else {
       
   110                     reject(new Error(data.message));
       
   111                 }
       
   112             });
       
   113 
       
   114             // Add a timeout in case the URL is completely wrong
       
   115             // or GitHub is too busy
       
   116             setTimeout(function () {
       
   117                 // Once a promise has been fulfilled or rejected it will never
       
   118                 // change its state again, so we can safely call reject() after
       
   119                 // some time. If it was already fulfilled or rejected, nothing will
       
   120                 // happen
       
   121                 reject(new Error('Timeout'));
       
   122             }, 10000);
       
   123         });
       
   124 
       
   125         // store the promise in the cache object
       
   126         cache[url] = promise;
       
   127 
       
   128         return promise;
       
   129     }
       
   130 
       
   131     return {
       
   132         getUser: function (name) {
       
   133             var url = getUserURL(name);
       
   134 
       
   135             if (cache[url]) {
       
   136                 // If we have already stored the promise in the cache we just return it
       
   137                 return cache[url];
       
   138             } else {
       
   139                 // fetch() will make a JSONP request, cache the promise and return it
       
   140                 return fetch(url);
       
   141             }
       
   142         }
       
   143     };
       
   144 }());
       
   145 
       
   146 
       
   147 var demoNode = Y.one('#demo');
       
   148 
       
   149 function log(text) {
       
   150     demoNode.append(Y.Node.create('<div></div>').set('text', text));
       
   151 }
       
   152 function showError(message) {
       
   153     demoNode.append(
       
   154         Y.Node.create('<div class="error"></div>').set('text', message)
       
   155     );
       
   156 }
       
   157 
       
   158 log('Fetching GitHub data for users: "yui", "yahoo" and "davglass"...')
       
   159 
       
   160 // requests is a regular promise
       
   161 var requests = Y.batch(GitHub.getUser('yui'), GitHub.getUser('yahoo'), GitHub.getUser('davglass'));
       
   162 // users is now an ArrayPromise
       
   163 var users = toArrayPromise(requests);
       
   164 
       
   165 // Transform the data into a list of names
       
   166 users.map(function (data) {
       
   167     log('Getting name for user "' + data.login + '"...')
       
   168     return data.name;
       
   169 }).filter(function (name) {
       
   170     log('Checking if the name "' + name + '" starts with "Y"...')
       
   171     return name.charAt(0) === 'Y';
       
   172 }).then(function (names) {
       
   173     log('Done!');
       
   174     return names;
       
   175 }).each(function (name, i) {
       
   176     log(i + '. ' + name);
       
   177 }).then(null, function (error) {
       
   178     // if there was an error in any step or request, it is automatically
       
   179     // passed around the promise chain so we can react to it at the end
       
   180     showError(error.message);
       
   181 });
       
   182 
       
   183 });
       
   184 </script>
       
   185 
       
   186 </div>
       
   187 
       
   188 <h2 id="subclassing-ypromise">Subclassing Y.Promise</h2>
       
   189 
       
   190 <p>
       
   191     You can subclass a YUI promise with <a href="../yui/yui-extend.html">Y.extend</a> the same way you would any other class. Keep in mind that Promise constructors take a function as a parameter so you need to call the superclass constructor in order for it to work.
       
   192 </p>
       
   193 
       
   194 <pre class="code prettyprint">function ArrayPromise() {
       
   195     ArrayPromise.superclass.constructor.apply(this, arguments);
       
   196 }
       
   197 Y.extend(ArrayPromise, Y.Promise);</pre>
       
   198 
       
   199 
       
   200 <h2 id="method-chaining">Method Chaining</h2>
       
   201 
       
   202 <p>
       
   203     Chaining promise methods is done by returning the result of calling the promise's <code>then()</code> method. <code>then()</code> <strong>always returns a promise of its same kind</strong>, so this will allow us to chain array operations as if they were real arrays.
       
   204 </p>
       
   205 <p>
       
   206     For the purpose of this example we will only add the <code>each</code>, <code>filter</code> and <code>map</code> methods from the <code>array-extras</code> module.
       
   207 </p>
       
   208 
       
   209 <pre class="code prettyprint">&#x2F;&#x2F; Although Y.Array.each does not return an array, for the purpose of this
       
   210 &#x2F;&#x2F; example we make it chainable by returning the same array
       
   211 ArrayPromise.prototype.each = function (fn, thisObj) {
       
   212     return this.then(function (array) {
       
   213         Y.Array.each(array, fn, thisObj);
       
   214         return array;
       
   215     });
       
   216 };
       
   217 
       
   218 &#x2F;&#x2F; Y.Array.map returns a new array, so we return the result of this.then()
       
   219 ArrayPromise.prototype.map = function (fn, thisObj) {
       
   220     return this.then(function (array) {
       
   221         &#x2F;&#x2F; By returning the result of Y.Array.map we are returning a new promise
       
   222         &#x2F;&#x2F; representing the new array
       
   223         return Y.Array.map(array, fn, thisObj);
       
   224     });
       
   225 };
       
   226 
       
   227 &#x2F;&#x2F; Y.Array.filter follows the same pattern as Y.Array.map
       
   228 ArrayPromise.prototype.filter = function (fn, thisObj) {
       
   229     return this.then(function (array) {
       
   230         return Y.Array.filter(array, fn, thisObj);
       
   231     });
       
   232 };</pre>
       
   233 
       
   234 
       
   235 <p>
       
   236     Finally we need a simple way to take a promise that we know contains an array and create an ArrayPromise with its value.
       
   237 </p>
       
   238 
       
   239 <pre class="code prettyprint">&#x2F;&#x2F; Takes any promise and returns an ArrayPromise
       
   240 function toArrayPromise(promise) {
       
   241     return new ArrayPromise(function (fulfill, reject) {
       
   242         promise.then(fulfill, reject);
       
   243     });
       
   244 }</pre>
       
   245 
       
   246 
       
   247 <h3 id="putting-our-class-to-action">Putting our Class to Action</h3>
       
   248 
       
   249 <p>
       
   250     There are many cases in which you would want to work on asynchronous array values. Performing more than one async operation at a time and dealing with the result is one common use case. <code>Y.batch</code> waits for many operations and returns a promise representing an array with the result of all the operations, so you could wrap it in an ArrayPromise to modify all those results.
       
   251 </p>
       
   252 
       
   253 <p>
       
   254     We will use the JSONP Cache from <a href="jsonp-cache.html">the previous example</a> and make several simultaneous requests.
       
   255 </p>
       
   256 
       
   257 <pre class="code prettyprint">log(&#x27;Fetching GitHub data for users: &quot;yui&quot;, &quot;yahoo&quot; and &quot;davglass&quot;...&#x27;)
       
   258 
       
   259 &#x2F;&#x2F; requests is a regular promise
       
   260 var requests = Y.batch(GitHub.getUser(&#x27;yui&#x27;), GitHub.getUser(&#x27;yahoo&#x27;), GitHub.getUser(&#x27;davglass&#x27;));
       
   261 &#x2F;&#x2F; users is now an ArrayPromise
       
   262 var users = toArrayPromise(requests);
       
   263 
       
   264 &#x2F;&#x2F; Transform the data into a list of names
       
   265 users.map(function (data) {
       
   266     log(&#x27;Getting name for user &quot;&#x27; + data.login + &#x27;&quot;...&#x27;)
       
   267     return data.name;
       
   268 }).filter(function (name) {
       
   269     log(&#x27;Checking if the name &quot;&#x27; + name + &#x27;&quot; starts with &quot;Y&quot;...&#x27;)
       
   270     return name.charAt(0) === &#x27;Y&#x27;;
       
   271 }).then(function (names) {
       
   272     log(&#x27;Done!&#x27;);
       
   273     return names;
       
   274 }).each(function (name, i) {
       
   275     log(i + &#x27;. &#x27; + name);
       
   276 }).then(null, function (error) {
       
   277     &#x2F;&#x2F; if there was an error in any step or request, it is automatically
       
   278     &#x2F;&#x2F; passed around the promise chain so we can react to it at the end
       
   279     showError(error.message);
       
   280 });</pre>
       
   281 
       
   282 
       
   283 <h2 id="full-example-code">Full Example Code</h2>
       
   284 
       
   285 <pre class="code prettyprint">&lt;script&gt;
       
   286 YUI().use(&#x27;promise&#x27;, &#x27;jsonp&#x27;, &#x27;node&#x27;, &#x27;array-extras&#x27;, function (Y) {
       
   287 
       
   288 function ArrayPromise() {
       
   289     ArrayPromise.superclass.constructor.apply(this, arguments);
       
   290 }
       
   291 Y.extend(ArrayPromise, Y.Promise);
       
   292 
       
   293 &#x2F;&#x2F; Although Y.Array.each does not return an array, for the purpose of this
       
   294 &#x2F;&#x2F; example we make it chainable by returning the same array
       
   295 ArrayPromise.prototype.each = function (fn, thisObj) {
       
   296     return this.then(function (array) {
       
   297         Y.Array.each(array, fn, thisObj);
       
   298         return array;
       
   299     });
       
   300 };
       
   301 
       
   302 &#x2F;&#x2F; Y.Array.map returns a new array, so we return the result of this.then()
       
   303 ArrayPromise.prototype.map = function (fn, thisObj) {
       
   304     return this.then(function (array) {
       
   305         &#x2F;&#x2F; By returning the result of Y.Array.map we are returning a new promise
       
   306         &#x2F;&#x2F; representing the new array
       
   307         return Y.Array.map(array, fn, thisObj);
       
   308     });
       
   309 };
       
   310 
       
   311 &#x2F;&#x2F; Y.Array.filter follows the same pattern as Y.Array.map
       
   312 ArrayPromise.prototype.filter = function (fn, thisObj) {
       
   313     return this.then(function (array) {
       
   314         return Y.Array.filter(array, fn, thisObj);
       
   315     });
       
   316 };
       
   317 
       
   318 &#x2F;&#x2F; Takes any promise and returns an ArrayPromise
       
   319 function toArrayPromise(promise) {
       
   320     return new ArrayPromise(function (fulfill, reject) {
       
   321         promise.then(fulfill, reject);
       
   322     });
       
   323 }
       
   324 
       
   325 
       
   326 &#x2F;&#x2F; A cache for GitHub user data
       
   327 var GitHub = (function () {
       
   328 
       
   329     var cache = {},
       
   330         githubURL = &#x27;https:&#x2F;&#x2F;api.github.com&#x2F;users&#x2F;{user}?callback={callback}&#x27;;
       
   331 
       
   332     function getUserURL(name) {
       
   333         return Y.Lang.sub(githubURL, {
       
   334             user: name
       
   335         });
       
   336     }
       
   337 
       
   338     &#x2F;&#x2F; Fetches a URL, stores a promise in the cache and returns it
       
   339     function fetch(url) {
       
   340         var promise = new Y.Promise(function (fulfill, reject) {
       
   341             Y.jsonp(url, function (res) {
       
   342                 var meta = res.meta,
       
   343                     data = res.data;
       
   344 
       
   345                 &#x2F;&#x2F; Check for a successful response, otherwise reject the
       
   346                 &#x2F;&#x2F; promise with the message returned by the GitHub API.
       
   347                 if (meta.status &gt;= 200 &amp;&amp; meta.status &lt; 300) {
       
   348                     fulfill(data);
       
   349                 } else {
       
   350                     reject(new Error(data.message));
       
   351                 }
       
   352             });
       
   353 
       
   354             &#x2F;&#x2F; Add a timeout in case the URL is completely wrong
       
   355             &#x2F;&#x2F; or GitHub is too busy
       
   356             setTimeout(function () {
       
   357                 &#x2F;&#x2F; Once a promise has been fulfilled or rejected it will never
       
   358                 &#x2F;&#x2F; change its state again, so we can safely call reject() after
       
   359                 &#x2F;&#x2F; some time. If it was already fulfilled or rejected, nothing will
       
   360                 &#x2F;&#x2F; happen
       
   361                 reject(new Error(&#x27;Timeout&#x27;));
       
   362             }, 10000);
       
   363         });
       
   364 
       
   365         &#x2F;&#x2F; store the promise in the cache object
       
   366         cache[url] = promise;
       
   367 
       
   368         return promise;
       
   369     }
       
   370 
       
   371     return {
       
   372         getUser: function (name) {
       
   373             var url = getUserURL(name);
       
   374 
       
   375             if (cache[url]) {
       
   376                 &#x2F;&#x2F; If we have already stored the promise in the cache we just return it
       
   377                 return cache[url];
       
   378             } else {
       
   379                 &#x2F;&#x2F; fetch() will make a JSONP request, cache the promise and return it
       
   380                 return fetch(url);
       
   381             }
       
   382         }
       
   383     };
       
   384 }());
       
   385 
       
   386 
       
   387 var demoNode = Y.one(&#x27;#demo&#x27;);
       
   388 
       
   389 function log(text) {
       
   390     demoNode.append(Y.Node.create(&#x27;&lt;div&gt;&lt;&#x2F;div&gt;&#x27;).set(&#x27;text&#x27;, text));
       
   391 }
       
   392 function showError(message) {
       
   393     demoNode.append(
       
   394         Y.Node.create(&#x27;&lt;div class=&quot;error&quot;&gt;&lt;&#x2F;div&gt;&#x27;).set(&#x27;text&#x27;, message)
       
   395     );
       
   396 }
       
   397 
       
   398 log(&#x27;Fetching GitHub data for users: &quot;yui&quot;, &quot;yahoo&quot; and &quot;davglass&quot;...&#x27;)
       
   399 
       
   400 &#x2F;&#x2F; requests is a regular promise
       
   401 var requests = Y.batch(GitHub.getUser(&#x27;yui&#x27;), GitHub.getUser(&#x27;yahoo&#x27;), GitHub.getUser(&#x27;davglass&#x27;));
       
   402 &#x2F;&#x2F; users is now an ArrayPromise
       
   403 var users = toArrayPromise(requests);
       
   404 
       
   405 &#x2F;&#x2F; Transform the data into a list of names
       
   406 users.map(function (data) {
       
   407     log(&#x27;Getting name for user &quot;&#x27; + data.login + &#x27;&quot;...&#x27;)
       
   408     return data.name;
       
   409 }).filter(function (name) {
       
   410     log(&#x27;Checking if the name &quot;&#x27; + name + &#x27;&quot; starts with &quot;Y&quot;...&#x27;)
       
   411     return name.charAt(0) === &#x27;Y&#x27;;
       
   412 }).then(function (names) {
       
   413     log(&#x27;Done!&#x27;);
       
   414     return names;
       
   415 }).each(function (name, i) {
       
   416     log(i + &#x27;. &#x27; + name);
       
   417 }).then(null, function (error) {
       
   418     &#x2F;&#x2F; if there was an error in any step or request, it is automatically
       
   419     &#x2F;&#x2F; passed around the promise chain so we can react to it at the end
       
   420     showError(error.message);
       
   421 });
       
   422 
       
   423 });
       
   424 &lt;&#x2F;script&gt;</pre>
       
   425 
       
   426 </div>
       
   427             </div>
       
   428         </div>
       
   429 
       
   430         <div class="yui3-u-1-4">
       
   431             <div class="sidebar">
       
   432                 
       
   433                     <div id="toc" class="sidebox">
       
   434                         <div class="hd">
       
   435                             <h2 class="no-toc">Table of Contents</h2>
       
   436                         </div>
       
   437 
       
   438                         <div class="bd">
       
   439                             <ul class="toc">
       
   440 <li>
       
   441 <a href="#subclassing-ypromise">Subclassing Y.Promise</a>
       
   442 </li>
       
   443 <li>
       
   444 <a href="#method-chaining">Method Chaining</a>
       
   445 <ul class="toc">
       
   446 <li>
       
   447 <a href="#putting-our-class-to-action">Putting our Class to Action</a>
       
   448 </li>
       
   449 </ul>
       
   450 </li>
       
   451 <li>
       
   452 <a href="#full-example-code">Full Example Code</a>
       
   453 </li>
       
   454 </ul>
       
   455                         </div>
       
   456                     </div>
       
   457                 
       
   458 
       
   459                 
       
   460                     <div class="sidebox">
       
   461                         <div class="hd">
       
   462                             <h2 class="no-toc">Examples</h2>
       
   463                         </div>
       
   464 
       
   465                         <div class="bd">
       
   466                             <ul class="examples">
       
   467                                 
       
   468                                     
       
   469                                         <li data-description="Wrapping async transactions with promises">
       
   470                                             <a href="basic-example.html">Wrapping async transactions with promises</a>
       
   471                                         </li>
       
   472                                     
       
   473                                 
       
   474                                     
       
   475                                         <li data-description="Extend Y.Promise to create classes that encapsulate standard transaction logic in descriptive method names">
       
   476                                             <a href="subclass-example.html">Subclassing Y.Promise</a>
       
   477                                         </li>
       
   478                                     
       
   479                                 
       
   480                                     
       
   481                                         <li data-description="Extend the Promise class to create your own Node plugin that chains transitions">
       
   482                                             <a href="plugin-example.html">Creating a Node Plugin that chains transitions</a>
       
   483                                         </li>
       
   484                                     
       
   485                                 
       
   486                             </ul>
       
   487                         </div>
       
   488                     </div>
       
   489                 
       
   490 
       
   491                 
       
   492             </div>
       
   493         </div>
       
   494     </div>
       
   495 </div>
       
   496 
       
   497 <script src="../assets/vendor/prettify/prettify-min.js"></script>
       
   498 <script>prettyPrint();</script>
       
   499 
       
   500 <script>
       
   501 YUI.Env.Tests = {
       
   502     examples: [],
       
   503     project: '../assets',
       
   504     assets: '../assets/promise',
       
   505     name: 'subclass-example',
       
   506     title: 'Subclassing Y.Promise',
       
   507     newWindow: '',
       
   508     auto:  false 
       
   509 };
       
   510 YUI.Env.Tests.examples.push('basic-example');
       
   511 YUI.Env.Tests.examples.push('subclass-example');
       
   512 YUI.Env.Tests.examples.push('plugin-example');
       
   513 
       
   514 </script>
       
   515 <script src="../assets/yui/test-runner.js"></script>
       
   516 
       
   517 
       
   518 
       
   519 </body>
       
   520 </html>