JavaScript的承诺很酷

“当我承诺某件事时,我永远都不会违反这一承诺。 决不。” ― 长发公主

许多语言都有有趣的方案库,称为promise,defers或future。 这些帮助将狂野的异步驯服为更平凡的顺序。 JavaScript许诺可以促进关注点的分离,而不是紧密耦合的接口。 本文是关于Promises / A变体JavaScript承诺的。 [ http://wiki.commonjs.org/wiki/Promises/A ]


承诺用例:

  • 执行规则
  • 多个远程验证
  • 处理超时
  • 远程数据请求
  • 动画
  • 将事件逻辑与应用逻辑分离
  • 消除厄运的回调三角形
  • 控制并行异步操作

JavaScript promise是将来要返回值的IOU。 它是具有明确定义的行为的数据对象。 一个承诺具有以下三种可能状态之一:

  1. 待定
  2. 被拒绝
  3. 解决

被拒绝或解决的诺言得以解决 。 一个承诺状态只能从未决状态变为已解决状态。 此后,其状态是不可变的。 承诺可以在其相关的行动解决之后很长时间才能实现。 有空时,我们可以多次提取结果。 我们通过调用promise.then()来实现这一点。 该呼叫将不会返回,除非或直到相关动作已解决为止。 我们可以流畅地兑现承诺。 链接的“ then”函数应各自返回一个promise,或让原始promise作为返回值。

通过这种范例,我们可以编写更多异步代码,就好像它是同步代码一样。 力量在于编写承诺任务:

  • 堆叠的任务:多个然后分散在代码中,但承诺相同。
  • 并行任务:多个promise返回一个promise。
  • 顺序任务: 答应……然后……答应
  • 这些的组合

为什么要增加这一层? 为什么我们不能只使用原始回调?

回调问题

回调适用于对简单的重复事件做出反应,例如基于单击启用表单值,或者用于存储REST调用的结果。 回调还通过使一个回调进行下一个REST调用,从而提供一个回调来进行下一个REST调用,从而使人们能够在链中进行编码,依此类推。 这趋向于如图1所示的厄运金字塔。在那里,代码水平增长比垂直增长快。 回调看似简单......直到我们需要的结果, 现在 ,在我们的代码下一步使用。

图1:Doom金字塔的​​反模式+下面的代码

图1:Doom金字塔的​​反模式+下面的代码

'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();

在图1中,我使用超时来模拟异步操作。 在那儿管理可能与下游动作可控地起作用的异常的想法是痛苦的。 当我们必须编写回调时,代码组织就会变得混乱。 图2显示了粘贴到NodeJS REPL中时将运行的模拟验证流程。 在下一节中,我们将其从“金字塔金字塔”模式迁移到顺序的Promise再现。

'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选项,那么我对整个验证的想法将类似于类似于同步编程的线性形式。 扁平的承诺链是可读的。 阅读...

使用承诺

图3显示了我将人为的验证重铸到了Promise链中。 它使用kew promise库。 Q库同样工作良好。 要尝试,请首先使用npm将kew库导入NodeJS,然后将代码加载到NodeJS REPL中。

'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
$

该代码有点“讲价”,但我认为它更易于理解和修改。 添加合理的错误处理更加容易。 在链内的链捕获错误的年底失效的电话,但我可以提供同时在任何一个拒绝处理,然后以应对其行动拒绝。

服务器或浏览器

承诺在浏览器以及NodeJS服务器中都是有用的。 以下URL http://jsfiddle.net/mauget/DnQDx/指向一个JSFiddle,该JSFiddle显示了如何在网页中使用单个Promise。 所有代码都可以在JSFiddle中进行修改。 浏览器输出的一种变化形式如图4所示。我操纵该动作以随机拒绝。 您可以尝试几次以得到相反的结果。 就像前面的NodeJS示例一样,将其扩展到多个Promise链将很简单。

图4:单一承诺

图4:单一承诺

并行承诺

考虑异步操作提供另一个异步操作。 让后者由三个并行的异步动作组成,这些动作又提供最终动作。 仅当所有并行子请求都解决时,它才会解决。 参见图5。这是从十二个MongoDB操作链的良好相遇中得到的启发。 有些人有资格并行运行。 我实现了承诺。

图5:异步动作的组成

图5:异步动作的组成


我们将如何在该图的中心行建模那些并行的承诺? 关键是大多数promise库具有一个all函数,该函数会生成数组中保存的子promise的parentpromise。 当所有孩子的诺言都解决时,父母的诺言就解决了。 如果孩子的一个诺言被拒绝,父母的诺言被拒绝。

图6显示了一个代码片段,该片段将十个文字转换为十个并行的承诺。 然后 ,只有当所有十个孩子都解决或任何一个孩子拒绝时, then结束才能完成。

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( . . .

以下URL http://jsfiddle.net/mauget/XKCy2/面向JSFiddle,该JSFiddle在浏览器中运行10个并行的Promise,随机拒绝或解决。 完整的代码在那里进行检查和假设更改。 重新运行,直到完成相反的操作。 图7显示了积极的结果。

图7:并行承诺的JSFiddle演示

图7:并行承诺的JSFiddle演示

生一个诺言

许多API返回具有then函数的promise –它们是thenable 。 通常我可以将一个then链接到一个可行的函数的结果上。 否则,$ q,mpromise,Q和kew库具有用于创建,拒绝或解决承诺的简单API。 参考部分中提供了每个库的API文档链接。 我通常不需要构造一个Promise,除了包装本文中忽略Promise的字面量和超时函数外。 请参阅我创建诺言的示例。

无极图书馆互通

大多数JavaScript承诺库在那时都可以互操作。 您可以从外国承诺中创建承诺,因为承诺可以包装任何类型的价值。 该工程跨库,支持即可从那时起,还有不同的承诺功能。 如果您需要一个库未包含的功能,则可以将库中的promise包装在具有所需功能的库中的新promise中。 例如,JQuery Promise在文献中有时会受到损害。 您可以立即将每个包装在Q,$ q,mpromise或kew promise中以在该库中进行操作。

最后

我写这篇文章的时候是一年前不愿接受承诺的人。 我只是想把工作做好。 由于误解了诺言,我不想学习新的API或不想破坏我的代码。 我曾经做错吗! 当我一毛钱下来时,我轻松地取得了可喜的成绩。

在本文中,我给出了单个诺言,诺言链和诺言的并行诺言的简单示例。 承诺不难使用。 如果我可以使用它们,任何人都可以。 为了充实概念,我鼓励您单击Promise专家提供的参考文献。 从Promises / A参考(JavaScript承诺的事实上的标准)开始。

如果您尚未直接使用承诺,请尝试一下。 解决:您将拥有良好的体验。 我承诺!

翻译自: https://www.javacodegeeks.com/2014/08/javascript-promises-are-cool.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值