`

JavaScript Promises 相当酷

 
阅读更多

And when I promise something, I never ever break that promise. Never.” ― Rapunzel

许多的语言,为了将异步模式处理得更像平常的顺序,都包含一种有趣的方案库,它们被称之为promises,deferreds,或者futures。JavaScript的promises ,可以促进关注点分离,以代替紧密耦合的接口。 本文讲的是基于Promises/A 标准的JavaScript promises。[http://wiki.commonjs.org/wiki/Promises/A]

Promise的用例: 

  • 执行规则 

  • 多个远程验证 

  • 超时处理 

  • 远程数据请求 

  • 动画 

  • 将事件逻辑从应用逻辑中解耦 

  • 消除回调函数的恐怖三角

  • 控制并行的异步操作

JavaScript promise是一个承诺将在未来返回值的对象。是具有良好定义的行为的数据对象。promise有三种可能的状态:

  1. Pending(待定)

  2. Rejected(拒绝)

  3. Resolved(已完成)

郭振华oo
郭振华oo
翻译于 2年前

2人顶

 

 翻译的不错哦!

一个已经拒绝或者完成的承诺属于已经解决的。一个承诺只能从待定状态变成已经解决的状态。之后,承诺的状态就不变了。承诺可以在它对应的处理完成之后很久还存在。也就是说,我们可以多次取得处理结果。我们通过调用promise.then()来取得结果,这个函数一直到承诺对应的处理结束才会返回。我们可以灵活的串联起一堆承诺。这些串联起来的“then”函数应该返回一个新的承诺或者最早的那个承诺。 
通过这个样式,我们可以像写同步代码一样来写非同步代码。主要是通过组合承诺来实现: 

  • 堆栈式任务:多处散落在代码中的,对应同一个承诺。

  • 并行任务:多个承诺返回同一个承诺。

  • 串行任务:一个承诺,然后接着执行另一个承诺。

  • 上面几种的组合。

为什么要这么麻烦?只用基本的回调函数不行吗? 

f4f
f4f
翻译于 2年前

1人顶

 

 翻译的不错哦!

回调函数的问题

回调函数适合简单的重复性事件,例如根据点击来让一个表单有效,或者保存一个REST调用的结果。回调函数还会使代码形成一个链,一个回调函数调用一个REST函数,并为REST函数设置一个新的回调函数,这个新的回调函数再调用另一个REST函数,依此类推。这就形成了一个如图1中的毁灭金字塔。代码的横向增长大于纵向的增长。回调函数看起来很简单,直到我们需要一个结果,而且是立刻就要,马上就用在下一行的计算中。 
图1 毁灭金字塔 
图1:毁灭金字塔 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'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();

 

f4f
f4f
翻译于 2年前

2人顶

 

 翻译的不错哦!

在图1中,我使用timeout来模拟异步操作。管理异常的方法是痛苦的,很容易玩漏下游的行为。当我们编写回调,那么代码组织变得混乱。图2显示了一个模拟验证流可以运行在NodeJS REPL。在下一节,我们将从pyramid-of-doom模式迁移到一个连续的promise。

Figure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'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执行的结果

1
2
3
4
5
6
7
$ node scripts/examp2b.js
1 Wait for it ...
2 result first
3 result second
4 result third
5 result fourth
$
多多de棉花糖
多多de棉花糖
翻译于 2年前

1人顶

 

 翻译的不错哦!

我曾经遇到一个AngularJS动态验证的情况,根据对应表的值,动态的限制表单项的值。限制项的有效值范围被定义在REST服务上。 

我写了一个调度器,根据请求的值,去操作函数栈,以避免回调嵌套。调度器从栈中弹出函数并执行。函数的回调会在结束时重新调用调度器,直到栈被清空。每次回调都记录所有从远程验证调用返回的验证错误。

我认为我写的玩意儿是一种反模式。如果我用Angular的$http调用提供的promise,在整个验证过程中我的思维会更近似线性形式,就像同步编程。平展的promise链是可读的。继续...

郭振华oo
郭振华oo
翻译于 2年前

1人顶

 

 翻译的不错哦!

使用Promises

图3显示了我将验证改写成promise链的样子。其中采用了kew promise库。Q库同样适用。要使用该库,首先使用npm将kew库导入到NodeJS,然后加载代码到NodeJS REPL。

Figure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
'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();

 

输出和使用嵌套回调时相同:

1
2
3
4
5
6
7
$ node scripts/examp2-pflat.js
1 Wait for it ...
2 result first
3 result second
4 result third
5 result fourth
$

 

该代码稍微“长高”了,但我认为更易于理解和修改。更易于加上适当的错误处理。在链的末尾调用fail用于捕获链中错误,但我也可以在任何一个then里面提供一个reject的处理函数做相应的处理。

郭振华oo
郭振华oo
翻译于 2年前

1人顶

 

 翻译的不错哦!

服务器 或 浏览器

Promises在浏览器中就像在NodeJS服务器中一样有效。下面的地址, http://jsfiddle.net/mauget/DnQDx/,指向JSFiddle的一个展示如何使用一个promise的web页面。 JSFiddle所有的代码是可修改的。浏览器输出的一个变化如图4所示。我故意操作随意动作。你可以试几次得到相反的结果。它是可以直接扩展到多个promise链, 就像前面NodeJS例子。

Figure 4

图4.单个promise

多多de棉花糖
多多de棉花糖
翻译于 2年前

1人顶

 

 翻译的不错哦!

并行 Promises

考虑一个异步操作喂养另一个异步操作。让后者包括三个并行异步行为,反过来,喂最后一个行动。只有当所有平行的子请求通过才能通过。如图5所示。这是灵感来自偶遇一打MongoDB操作。有些是合格的并行操作。我实现了promises的流流程图。

Figure 5

图5:异步操作的结构

我们怎么会模拟那些在该图中心行的并行promises?关键是,最大的promise库有一个全功能,它产生一个包含一组子promises的父promie。当所有的子promises通过,父promise通过。如果有一个子promise拒绝,父promise拒绝。

多多de棉花糖
多多de棉花糖
翻译于 2年前

1人顶

 

 翻译的不错哦!

图6显示了一个代码片段,让十个并行的promises每个都包含一个文字promise。只有当十个子类通过或如果任何子类拒绝,最后的then方法才能完成。

Figure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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条件。重新运行,直到你得到一个相反的完成。图7显示了积极的结果。

Figure 7

图7:JSFiddle并行promises样例

多多de棉花糖
多多de棉花糖
翻译于 2年前

1人顶

 

 翻译的不错哦!

孕育 Promise

许多api返回的promise都有一个then函数——他们是thenable。通常我只是通过then处理thenable函数的结果。然而,$q,mpromise,和kew库拥有同样的API用于创建,拒绝,或者通过promise。这里有API文档链接到每个库的引用部分。我通常不需要构造一个promise,除了本文中的包装promise的未知描述和timeout函数。请参考哪些我创建的promises。

多多de棉花糖
多多de棉花糖
翻译于 2年前

1人顶

 

 翻译的不错哦!

Promise库互操作

大多数JavaScript promise库在then级别进行互操作。你可以从一个外部的promise创建一个promise,因为promise可以包装任何类型的值。then可以支持跨库工作。除了then,其他的promise函数则可能不同。如果你需要一个你的库不包含的函数,你可以将一个基于你的库的promise包装到一个新的,基于含有你所需函数的库创建的promise里面。例如,JQuery的promise有时为人所诟病。那么你可以将其包装到Q,$q,mpromise,或者kew库的promise中进行操作。

郭振华oo
郭振华oo
翻译于 2年前

1人顶

 

 翻译的不错哦!

结语

现在我写了这篇文章,而一年前我却是犹豫要不要拥抱promise的那个。我只是单纯地想完成一项工作。 我不想学习新的API,或是打破我原来的代码(因为误解了promise)。我曾经如此错误地认为!当我下了一点注时,就轻易就赢得了可喜的成果。

在这篇文章中,我已经简单给出了一个单一的promise,promise链,和一个并行的promise的promise的的例子。 Promises不难使用。如果我可以使用它们,任何人都可以。 要查看完整的概念,我支持你点击专家写的参考指南。从Promises/A 的参考开始,从事实上的标准JavaScript的Promise 开始。

如果你还没有直接使用的promise,试一下。下定决心:你会有一个不错的体验。我保证!

– Lou Mauget, asktheteam@keyholesoftware.com

参考链接

分享到:
评论

相关推荐

    Mastering JavaScript Promises(PACKT,2015)

    To overcome these limitations, a concept called JavaScript promises is rising rapidly in popularity. Promises makes writing complex logics more manageable and easy. This book starts with an ...

    Understanding JavaScript Promises 理解JavaScript Promises

    Which parts of promises are synchronous and which are asynchronous How to effectively use promise chains to simplify your code The best way to catch errors in promises How to create your own promises ...

    Mastering Javascript Promises

    JavaScript. It has an evolving context that will lead you from a beginner's level to the master level of promises. Every chapter of this book will give you an outline to achieve a specific goal that ...

    Promises的JavaScript实现promisejs.zip

    promise.js 是 Promises 的轻量级 JavaScript 实现。 Promises 提供了 callback-passing 的替代方案,异步函数返回一个 Promise 对象可附加到 callbacks 中。 示例代码: function asyncfoo() { var p = new ...

    JavaScript中Promises/A+规范的实现

    JavaScript中的Promise是异步编程的一种解决方案,它解决了回调地狱(Callback Hell)问题,使得复杂的异步操作更加清晰、可读性更强。Promise/A+规范是Promise实现的一个标准,旨在提供一致性和可移植性。该规范...

    promisespromises:JavaScript Promises 幻灯片

    JavaScript Promises 是一种异步编程解决方案,用于处理可能需要时间完成的操作,如网络请求或文件读取。在JavaScript中,Promise对象代表一个异步操作的最终完成(或失败)及其结果值。Promises的设计思想是解决回...

    JavaScript Concurrency pdf 无水印 0分

    Explore the latest tools and techniques at the forefront of concurrent programming, including JavaScript promises, web workers, and generators Learn how concurrent and parallel programming can help ...

    如何利用Promises编写更优雅的JavaScript代码_.docx

    总的来说,Promises 是现代 JavaScript 开发中处理异步操作的关键工具。它们提高了代码的可读性、可维护性和错误处理能力,使得编写优雅、安全的 JavaScript 代码变得更加容易。通过学习和熟练掌握 Promises,你可以...

    pinkyPromise:一个简单的 JavaScript Promises 库

    pinkyPromise是一个超简单的 promises 库,旨在提供对 JavaScript promises 内部工作的洞察。 它不符合规范或用于生产用途。 原始博客文章: 。 用法 // take an async function function incrementAsync ( n , cb...

    从零开始学JavaScript

    这涉及到回调函数、Promises、async/await等概念,这些是处理诸如网络请求、文件读写等耗时操作的关键。 6. ES6新增特性:ECMAScript是JavaScript的标准化规范。ECMAScript 6(ES6)引入了许多新特性,如箭头函数、...

    Javascript.info Ebook Part 1 The JavaScript language (2019).pdf

    《*** Ebook Part 1 The JavaScript language (2019)》是一本关于JavaScript语言的现代教程,其内容涵盖了JavaScript的基础和进阶知识,特别是DOM(文档对象模型)的相关知识。本书对于初学者和有经验的开发者都有很...

    Node.js-make-promises-safe一个让promises的使用免于内存泄漏的node.js模块

    ```javascript const { makePromisesSafe } = require('make-promises-safe'); // 启动全局的unhandled rejection处理器 makePromisesSafe(); ``` 3. 此后,所有未被捕获的Promise拒绝错误都会被记录,并在...

    Mastering.JavaScript.High.Performance.1784397296

    Next, you will move on to learn about DOM optimization, JavaScript promises, and web workers to better break up your large codebase. You will also learn about JavaScript performance on mobile ...

    Mastering JavaScript High Performance(PACKT,2015)

    Next, you will move on to learn about DOM optimization, JavaScript promises, and web workers to better break up your large codebase. You will also learn about JavaScript performance on mobile ...

    HTTP-Library-using-Promises:在JavaScript Promises的帮助下使用Fake API实现HTTP库

    在这个特定的项目"HTTP-Library-using-Promises"中,开发人员选择利用JavaScript的Promise特性来构建一个HTTP库,同时使用了Fake API以模拟实际的网络服务。Promise是ES6引入的一种异步编程解决方案,它允许我们以一...

    Programming JavaScript Applications

    , AMD, Asynchronous Operations, Callbacks, Promises and Deferreds, Code Quality, Function Polymorphism, Function Scope, Hoisting and Closures, Functional Programming and Stateless Functions, ...

Global site tag (gtag.js) - Google Analytics