遇到了一些关于Nodejs Callback的坑,于是只能去翻译一些文章来解决当前的问题。
原文: JavaScript Promises Are Cool
“And when I promise something, I never ever break that promise. Never.” ― Rapunzel
多数语言都有有趣的方案的库被称为Promises, deferreds, 又或者是future. 这些库可以帮助我们驾驭这些原生的异步成更平凡的顺序。 JavaScript的Promises可以促进关注点分离代替紧密耦合接口。
适用于Promises的情形:
JavaScript Promises是会在将来返回一个值的欠条。它是具有良好定义的行为的数据对象。Promises有三种可能的状态之一:
一个rejected或resolved promise是固定的。一个Promise状态只能从pending到固定的。此后它的状态是不可改变的。一个Promise可以在他关联的动作固定之后保持相当长时间。需要时,我们可以多次提取结果。我们可以通过调用promise.then()实现这一点。仅当相关的动作执行完,该调用才会返回。同时,我们可以流畅使用链式Promise。每个链式的"Then"功能要么返回一个Promise,要么返回原始的Promise值。
Through this paradigm we can write asynchronous code more as if it were synchronous code. The power lies in composing promise tasks:
Stacked tasks: multiple thens scattered in the code, but on same promise. Parallel tasks: multiple promises return a single promise. Sequential tasks: promise … then … promise Combinations of these. Why this extra layer? Why can’t we just use raw callbacks?
通过这个范例,我们可以写出更多的异步代码,同时看上去像是同步代码。动力就在于构建Promise的任务:
为什么这些额外的层?为什么我们不能只使用原始回调?
对于由点击触发的表单值与存储REST响应的结果之类的简单的经常性事件来说,Callbacks是一个不错的解决方案。Callbacks也诱使一个代码在一个链式具有一个REST的回调,接着又提供了一个回调函数,以调用下一个REST,如此下去。这一趋向金字塔厄运,如图1所示。在那里,代码就比它长的垂直速度的水平。回调似乎简单的……直到我们需要一个结果,现在,使用我们接下来的代码。
'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();
在上面的代码中,我使用了超时mock异步行为。管理exceptions有可能发挥控制与下游的行为概念是痛苦的。当我们编写Callback,代码的组织就会变得混乱。下面的代码显示了一个模拟的验证流程,将运行,当粘贴到Nodejs REPL。我们将把它从下一段中,一个顺序的Promise pyramid-of-doom。
'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服务裁定强制每个项目的有效价值。我写的一摞功能基础上被要求值运行调度避免嵌套的回调。调度器将弹出从堆栈的函数并执行它。这个函数的回调会不断调用我的调度员,直到堆栈清空完成。每个Callback会记录任何的从远程验证调用的验证错误。
我认为写的代码是一个反模式。如果我用Angluar的$HTTP调用提供的Promise的选择,我在想整个验证会类似于线性形式——类似于同步编程。平展的Promise的链易读的。.
在下面的代码中,我将我们的验证改成了一个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
$
该代码是稍微更高,但我认为这是比较容易理解和修改。添加错误处理更合理。在链的末端不叫捕获错误的内链,但我也可以提供一个拒绝处理程序在任何后处理其行动的一种排斥。
考虑一个异步操作进另一个异步动作。让后者包括三个并行异步行为,反过来,进到最后动作。它解决了只有当所有的子请求解决并行。见下面的图,这是受来自于十二个的MongoDB链式操作的启发。一些有资格的去操作并行。我实现的Promsie的流
Figure 6 shows a code fragment that makes ten literals into a promise of ten parallel promises. The then at the end completes only when all ten children resolve or if any child rejects.
我们将如何在图中执行模型的并行的Promise?关键是有一个全功能的最有希望的Promise库将产生的有很多子promises的父Promise在一个数组中举行。当所有的子Promise解决,父Promise也就解决了。如果一个子Promise拒绝,那么父Promise也将拒绝。 图6显示了一个代码片段,使十个字面值为十个平行的Promise。然后只有当所有十个子解决或任何子Promise拒绝才算完成。
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( . . .
The following URL, , targets a JSFiddle that runs 10 parallel promises in a browser, rejecting or resolving at random. The complete code is there for inspection and what-if changes. Rerun until you get an opposite completion. Figure 7 shows the positive result.
代码可以见http://jsfiddle.net/mauget/XKCy2/,在浏览器里运行10个并行的Promise, 解决或者拒绝是随机的。完整的代码进行检查,如果有变化。会一直Rerun直到你得到一个相反的完成。下面是一个运气不错的结果。
许多API返回一个Promise有一个then函数–他们是可以then。通常我可以链到thenable函数的结果。另外,$Q,和mpromise,kew库有一个简单的API来创建,拒绝,或解决一个Promise。在参考部分的每个库API文档的链接。我通常不需要构建一个Promise,除本条包装答应无知的文字和超时功能。盾看我创建了Promises的例子。
大多数JavaScript库互操作的Promise在then层。您可以从一个外部的Promise创建别一个Promise,因为Promise可以用任意值。而这可以通过库来支持then。除了那Promis,他们有不同的功能。如果你需要一个函数库而你的库不包含,你可以把你的Promise与库包装起来,构建成一个有你想要的功能的一个新的Promise。例如,jQuery的Promise是在文献中有时诬蔑。你可以将每个正确工作的Q,$Q,mpromise,或kew,包装起来去在库中操作。
写这篇文章的人一年前犹豫的拥抱的Promise。我只是想得到一份工作。我不想学习一种新的API或机会打破我的代码由于误解的Promise。是我错了!当我下车,一分钱,我轻松地取得了可喜的成果。
在这篇文章中,我已经给了一个Promise,简单的例子,一个Promise链,和一个平行的Promise,Promise。Promise是不难用的。如果我能使用他们,任何人都可以。肉的概念,我鼓励你点击了提供了参考专家写的Promise。从开始的Promise/A 参考,JavaScript的事实上的标准的Promise。
如果你没有直接使用的Promise,给他们一个尝试。解决:你会有一个很好的经验。我的承诺!
围观我的Github Idea墙, 也许,你会遇到心仪的项目