这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
这篇文章主要介绍了 JavaScript 中的 Promise 使用详解, promise 对象是 JS 进阶学习中的重要知识点, 需要的朋友可以参考下
许多的语言,为了将异步模式处理得更像平常的顺序,都包含一种有趣的方案库,它们被称之为 promises,deferreds,或者 futures。JavaScript 的 promises ,可以促进关注点分离,以代替紧密耦合的接口。 本文讲的是基于 Promises/A 标准的 JavaScript promises。[http://wiki.commonjs.org/wiki/Promises/A]
Promise 的用例:
JavaScript promise 是一个承诺将在未来返回值的对象。是具有良好定义的行为的数据对象。promise 有三种可能的状态:
一个已经拒绝或者完成的承诺属于已经解决的。一个承诺只能从待定状态变成已经解决的状态。之后,承诺的状态就不变了。承诺可以在它对应的处理完成之后很久还存在。也就是说,我们可以多次取得处理结果。我们通过调用 promise.then() 来取得结果,这个函数一直到承诺对应的处理结束才会返回。我们可以灵活的串联起一堆承诺。这些串联起来的"then" 函数应该返回一个新的承诺或者最早的那个承诺。 通过这个样式,我们可以像写同步代码一样来写非同步代码。主要是通过组合承诺来实现:
为什么要这么麻烦?只用基本的回调函数不行吗?
回调函数的问题
回调函数适合简单的重复性事件,例如根据点击来让一个表单有效,或者保存一个 REST 调用的结果。回调函数还会使代码形成一个链,一个回调函数调用一个 REST 函数,并为 REST 函数设置一个新的回调函数,这个新的回调函数再调用另一个 REST 函数,依此类推。代码的横向增长大于纵向的增长。回调函数看起来很简单,直到我们需要一个结果,而且是立刻就要,马上就用在下一行的计算中。
- 'use strict';
- var i = 0;
- function log(data) {
- console.log('%d %s', ++i, data);
- };
- function validate() {
- log("Wait for it ...");
- // Sequence of four Long-running async activities
- setTimeout(function() {
- log('result first');
- setTimeout(function() {
- log('result second');
- setTimeout(function() {
- log('result third');
- setTimeout(function() {
- log('result fourth')
- },
- 1000);
- },
- 1000);
- },
- 1000);
- },
- 1000);
- };
- validate();
我使用 timeout 来模拟异步操作。管理异常的方法是痛苦的,很容易玩漏下游行为。当我们编写回调, 那么代码组织变得混乱。图 2 显示了一个模拟验证流可以运行在 NodeJS REPL。在下一节,我们将从 pyramid-of-doom 模式迁移到一个连续的 promise。
Figure
- 'use strict';
- var i = 0;
- function log(data) {
- console.log('%d %s', ++i, data);
- };
- // Asynchronous fn executes a callback result fn
- function async(arg, callBack) {
- setTimeout(function() {
- log('result ' + arg);
- callBack();
- },
- 1000);
- };
- function validate() {
- log("Wait for it ...");
- // Sequence of four Long-running async activities
- async('first',
- function() {
- async('second',
- function() {
- async('third',
- function() {
- async('fourth',
- function() {});
- });
- });
- });
- };
- validate();
在 NodeJS REPL 执行的结果
- $ node scripts/examp2b.js
- 1 Wait for it ...
- 2 result first
- 3 result second
- 4 result third
- 5 result fourth
- $
我曾经遇到一个 AngularJS 动态验证的情况,根据对应表的值,动态的限制表单项的值。限制项的有效值范围被定义在 REST 服务上。
我写了一个调度器,根据请求的值,去操作函数栈,以避免回调嵌套。调度器从栈中弹出函数并执行。函数的回调会在结束时重新调用调度器,直到栈被清空。每次回调都记录所有从远程验证调用返回的验证错误。
我认为我写的玩意儿是一种反模式。如果我用 Angular 的 $http 调用提供的 promise,在整个验证过程中我的思维会更近似线性形式,就像同步编程。平展的 promise 链是可读的。继续... 使用 Promises
其中采用了 kew promise 库。Q 库同样适用。要使用该库,首先使用 npm 将 kew 库导入到 NodeJS,然后加载代码到 NodeJS REPL。
Figure
- 'use strict';
- var Q = require('kew');
- var i = 0;
- function log(data) {console.log('%d %s', ++i, data); };
- // Asynchronous fn returns a promise
- function async(arg) {
- var deferred = Q.defer();
- setTimeout(function () {
- deferred.resolve('result ' + arg);\
- }, 1000);
- return deferred.promise;
- };
- // Flattened promise chain
- function validate() {
- log("Wait for it ...");
- async('first').then(function(resp){
- log(resp);
- return async('second');
- })
- .then(function(resp){
- log(resp);
- return async('third')
- })
- .then(function(resp){
- log(resp);
- return async('fourth');
- })
- .then(function(resp){
- log(resp);
- }).fail(log);
- };
- validate();
输出和使用嵌套回调时相同:
- $ node scripts/examp2-pflat.js
- 1 Wait for it ...
- 2 result first
- 3 result second
- 4 result third
- 5 result fourth
- $
该代码稍微 "长高" 了,但我认为更易于理解和修改。更易于加上适当的错误处理。在链的末尾调用 fail 用于捕获链中错误,但我也可以在任何一个 then 里面提供一个 reject 的处理函数做相应的处理。
服务器 或 浏览器
Promises 在浏览器中就像在 NodeJS 服务器中一样有效。下面的地址, http://jsfiddle.net/mauget/DnQDx/,指向 JSFiddle 的一个展示如何使用一个 promise 的 web 页面。 JSFiddle 所有的代码是可修改的。我故意操作随意动作。你可以试几次得到相反的结果。它是可以直接扩展到多个 promise 链, 就像前面 NodeJS 例子。
并行 Promises
考虑一个异步操作喂养另一个异步操作。让后者包括三个并行异步行为, 反过来, 喂最后一个行动。只有当所有平行的子请求通过才能通过。这是灵感来自偶遇一打 MongoDB 操作。有些是合格的并行操作。我实现了 promises 的流流程图。
我们怎么会模拟那些在该图中心行的并行 promises?关键是,最大的 promise 库有一个全功能,它产生一个包含一组子 promises 的父 promie。当所有的子 promises 通过,父 promise 通过。如果有一个子 promise 拒绝,父 promise 拒绝。
让十个并行的 promises 每个都包含一个文字 promise。只有当十个子类通过或如果任何子类拒绝,最后的 then 方法才能完成。
Figure
- var promiseVals = ['To ', 'be, ', 'or ',
- 'not ', 'to ', 'be, ', 'that ',
- 'is ', 'the ', 'question.'];
- var startParallelActions = function (){
- var promises = [];
- // Make an asynchronous action from each literal
- promiseVals.forEach(function(value){
- promises.push(makeAPromise(value));
- });
- // Consolidate all promises into a promise of promises
- return Q.all(promises);
- };
- startParallelActions ().then( . . .
下面的地址, http://jsfiddle.net/mauget/XKCy2/,针对 JSFiddle 在浏览器中运行十个并行 promises, 随机的拒绝或通过。这里有完整的代码用于检查和变化 if 条件。重新运行, 直到你得到一个相反的完成。
孕育 Promise
许多 api 返回的 promise 都有一个 then 函数——他们是 thenable。通常我只是通过 then 处理 thenable 函数的结果。然而,$q,mpromise,和 kew 库拥有同样的 API 用于创建,拒绝,或者通过 promise。这里有 API 文档链接到每个库的引用部分。我通常不需要构造一个 promise, 除了本文中的包装 promise 的未知描述和 timeout 函数。请参考哪些我创建的 promises。
Promise 库互操作
大多数 JavaScript promise 库在 then 级别进行互操作。你可以从一个外部的 promise 创建一个 promise,因为 promise 可以包装任何类型的值。then 可以支持跨库工作。除了 then,其他的 promise 函数则可能不同。如果你需要一个你的库不包含的函数,你可以将一个基于你的库的 promise 包装到一个新的,基于含有你所需函数的库创建的 promise 里面。例如,JQuery 的 promise 有时为人所诟病。那么你可以将其包装到 Q,$q,mpromise,或者 kew 库的 promise 中进行操作。 结语
现在我写了这篇文章,而一年前我却是犹豫要不要拥抱 promise 的那个。我只是单纯地想完成一项工作。 我不想学习新的 API,或是打破我原来的代码(因为误解了 promise)。我曾经如此错误地认为!当我下了一点注时,就轻易就赢得了可喜的成果。
在这篇文章中,我已经简单给出了一个单一的 promise,promise 链,和一个并行的 promise 的 promise 的的例子。 Promises 不难使用。如果我可以使用它们,任何人都可以。 要查看完整的概念,我支持你点击专家写的参考指南。从 Promises/A 的参考开始,从事实上的标准 JavaScript 的 Promise 开始。
来源: http://www.phperz.com/article/17/0426/270701.html