zoukankan      html  css  js  c++  java
  • JS

    第一部分:

    JS - Promise使用详解1(基本概念、使用优点)

    一、promises相关概念

    promises 的概念是由 CommonJS 小组的成员在 Promises/A 规范中提出来的。
     

    1,then()方法介绍

    根据 Promise/A 规范,promise 是一个对象,只需要 then 这一个方法。then 方法带有如下三个参数:
    • 成功回调
    • 失败回调
    • 前进回调(规范没有要求包括前进回调的实现,但是很多都实现了)。
    一个全新的 promise 对象从每个 then 的调用中返回。
     

    2,Promise对象状态

    Promise 对象代表一个异步操作,其不受外界影响,有三种状态:
    • Pending(进行中、未完成的)
    • Resolved(已完成,又称 Fulfilled)
    • Rejected(已失败)。
    (1)promise 从未完成的状态开始,如果成功它将会是完成态,如果失败将会是失败态。
    (2)当一个 promise 移动到完成态,所有注册到它的成功回调将被调用,而且会将成功的结果值传给它。另外,任何注册到 promise 的成功回调,将会在它已经完成以后立即被调用。
    (3)同样的,当一个 promise 移动到失败态的时候,它调用的是失败回调而不是成功回调。
    (4)对包含前进特性的实现来说,promise 在它离开未完成状态以前的任何时刻,都可以更新它的 progress。当 progress 被更新,所有的前进回调(progress callbacks)会被传递以 progress 的值,并被立即调用。前进回调被以不同于成功和失败回调的方式处理;如果你在一个 progress 更新已经发生以后注册了一个前进回调,新的前进回调只会在它被注册以后被已更新的 progress 调用。
    (5)注意:只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
     

    3,Promise/A规范图解

    原文:JS - Promise使用详解1(基本概念、使用优点)

    4,目前支持Promises/A规范的库

    • Q:可以在NodeJS 以及浏览器上工作,与jQuery兼容,可以通过消息传递远程对象。
    • RSVP.js:一个轻量级的库,它提供了组织异步代码的工具。
    • when.js:体积小巧,使用方便。
    • NodeJS的Promise
    • jQuery 1.5:据说是基于“CommonJS Promises/A”规范
    • WinJS / Windows 8 / Metro
     

    二、使用promises的优势

    1,解决回调地狱(Callback Hell)问题

    (1)有时我们要进行一些相互间有依赖关系的异步操作,比如有多个请求,后一个的请求需要上一次请求的返回结果。过去常规做法只能 callback 层层嵌套,但嵌套层数过多的话就会有 callback hell 问题。比如下面代码,可读性和维护性都很差的。
    firstAsync(function(data){
        //处理得到的 data 数据
        //....
        secondAsync(function(data2){
            //处理得到的 data2 数据
            //....
            thirdAsync(function(data3){
                  //处理得到的 data3 数据
                  //....
            });
        });
    });

    (2)如果使用 promises 的话,代码就会变得扁平且更可读了。前面提到 then 返回了一个 promise,因此我们可以将 then 的调用不停地串连起来。其中 then 返回的 promise 装载了由调用返回的值。

    firstAsync()
    .then(function(data){
        //处理得到的 data 数据
        //....
        return secondAsync();
    })
    .then(function(data2){
        //处理得到的 data2 数据
        //....
        return thirdAsync();
    })
    .then(function(data3){
        //处理得到的 data3 数据
        //....
    });

    2,更好地进行错误捕获 

    多重嵌套 callback 除了会造成上面讲的代码缩进问题,更可怕的是可能会造成无法捕获异常或异常捕获不可控。
    (1)比如下面代码我们使用 setTimeout 模拟异步操作,在其中抛出了个异常。但由于异步回调中,回调函数的执行栈与原函数分离开,导致外部无法抓住异常。
     1 function fetch(callback) {
     2     setTimeout(() => {
     3         throw Error('请求失败')
     4     }, 2000)
     5 }
     6  
     7 try {
     8     fetch(() => {
     9         console.log('请求处理') // 永远不会执行
    10     })
    11 } catch (error) {
    12     console.log('触发异常', error) // 永远不会执行
    13 }
    14  
    15 // 程序崩溃
    16 // Uncaught Error: 请求失败
    (2)如果使用 promises 的话,通过 reject 方法把 Promise 的状态置为 rejected,这样我们在 then 中就能捕捉到,然后执行“失败”情况的回调。
     1 function fetch(callback) {
     2     return new Promise((resolve, reject) => {
     3         setTimeout(() => {
     4              reject('请求失败');
     5         }, 2000)
     6     })
     7 }
     8  
     9  
    10 fetch()
    11 .then(
    12     function(data){
    13         console.log('请求处理');
    14         console.log(data);
    15     },
    16     function(reason, data){
    17         console.log('触发异常');
    18         console.log(reason);
    19     }
    20 );
    当然我们在 catch 方法中处理 reject 回调也是可以的。
     1 function fetch(callback) {
     2     return new Promise((resolve, reject) => {
     3         setTimeout(() => {
     4              reject('请求失败');
     5         }, 2000)
     6     })
     7 }
     8  
     9  
    10 fetch()
    11 .then(
    12     function(data){
    13         console.log('请求处理');
    14         console.log(data);
    15     }
    16 )
    17 .catch(function(reason){
    18     console.log('触发异常');
    19     console.log(reason);
    20 });

    第二部分:

    JS - Promise使用详解2(ES6中的Promise)

    2015年6月, ES2015(即 ECMAScript 6、ES6) 正式发布。其中 Promise 被列为正式规范,成为 ES6 中最重要的特性之一。

     

    1,then()方法

    简单来讲,then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
    而 Promise 的优势就在于这个链式调用。我们可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作。
     
    (1)下面通过样例作为演示,我们定义做饭、吃饭、洗碗(cook、eat、wash)这三个方法,它们是层层依赖的关系,下一步的的操作需要使用上一部操作的结果。(这里使用 setTimeout 模拟异步操作)
     1 //做饭
     2 function cook(){
     3     console.log('开始做饭。');
     4     var p = new Promise(function(resolve, reject){        //做一些异步操作
     5         setTimeout(function(){
     6             console.log('做饭完毕!');
     7             resolve('鸡蛋炒饭');
     8         }, 1000);
     9     });
    10     return p;
    11 }
    12  
    13 //吃饭
    14 function eat(data){
    15     console.log('开始吃饭:' + data);
    16     var p = new Promise(function(resolve, reject){        //做一些异步操作
    17         setTimeout(function(){
    18             console.log('吃饭完毕!');
    19             resolve('一块碗和一双筷子');
    20         }, 2000);
    21     });
    22     return p;
    23 }
    24  
    25 function wash(data){
    26     console.log('开始洗碗:' + data);
    27     var p = new Promise(function(resolve, reject){        //做一些异步操作
    28         setTimeout(function(){
    29             console.log('洗碗完毕!');
    30             resolve('干净的碗筷');
    31         }, 2000);
    32     });
    33     return p;
    34 }

    (2)使用 then 链式调用这三个方法:

     1 cook()
     2 .then(function(data){
     3     return eat(data);
     4 })
     5 .then(function(data){
     6     return wash(data);
     7 })
     8 .then(function(data){
     9     console.log(data);
    10 });

    当然上面代码还可以简化成如下:

    1 cook()
    2 .then(eat)
    3 .then(wash)
    4 .then(function(data){
    5     console.log(data);
    6 });

    (3)运行结果如下:

    原文:JS - Promise使用详解2(ES6中的Promise)

    2,reject()方法

    上面样例我们通过 resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。
    而 reject 方法就是把 Promise 的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)。
     
    (1)下面同样使用一个样例做演示
     1 //做饭
     2 function cook(){
     3     console.log('开始做饭。');
     4     var p = new Promise(function(resolve, reject){        //做一些异步操作
     5         setTimeout(function(){
     6             console.log('做饭失败!');
     7             reject('烧焦的米饭');
     8         }, 1000);
     9     });
    10     return p;
    11 }
    12  
    13 //吃饭
    14 function eat(data){
    15     console.log('开始吃饭:' + data);
    16     var p = new Promise(function(resolve, reject){        //做一些异步操作
    17         setTimeout(function(){
    18             console.log('吃饭完毕!');
    19             resolve('一块碗和一双筷子');
    20         }, 2000);
    21     });
    22     return p;
    23 }
    24  
    25 cook()
    26 .then(eat, function(data){
    27   console.log(data + '没法吃!');
    28 })

    运行结果如下:

    原文:JS - Promise使用详解2(ES6中的Promise)
    (2)如果我们只要处理失败的情况可以使用 then(null, ...),或是使用接下来要讲的 catch 方法。
    1 cook()
    2 .then(null, function(data){
    3   console.log(data + '没法吃!');
    4 })

    3,catch()方法

    (1)它可以和 then 的第二个参数一样,用来指定 reject 的回调

    1 cook()
    2 .then(eat)
    3 .catch(function(data){
    4     console.log(data + '没法吃!');
    5 });

    (2)它的另一个作用是,当执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么也不会报错卡死 js,而是会进到这个 catch 方法中。

     1 //做饭
     2 function cook(){
     3     console.log('开始做饭。');
     4     var p = new Promise(function(resolve, reject){        //做一些异步操作
     5         setTimeout(function(){
     6             console.log('做饭完毕!');
     7             resolve('鸡蛋炒饭');
     8         }, 1000);
     9     });
    10     return p;
    11 }
    12  
    13 //吃饭
    14 function eat(data){
    15     console.log('开始吃饭:' + data);
    16     var p = new Promise(function(resolve, reject){        //做一些异步操作
    17         setTimeout(function(){
    18             console.log('吃饭完毕!');
    19             resolve('一块碗和一双筷子');
    20         }, 2000);
    21     });
    22     return p;
    23 }
    24  
    25 cook()
    26 .then(function(data){
    27     throw new Error('米饭被打翻了!');
    28     eat(data);
    29 })
    30 .catch(function(data){
    31     console.log(data);
    32 });

    运行结果如下:

    原文:JS - Promise使用详解2(ES6中的Promise)
    这种错误的捕获是非常有用的,因为它能够帮助我们在开发中识别代码错误。比如,在一个 then() 方法内部的任意地方,我们做了一个 JSON.parse() 操作,如果 JSON 参数不合法那么它就会抛出一个同步错误。用回调的话该错误就会被吞噬掉,但是用 promises 我们可以轻松的在 catch() 方法里处理掉该错误。
     
    (3)还可以添加多个 catch,实现更加精准的异常捕获。
     1 somePromise.then(function() {
     2  return a();
     3 }).catch(TypeError, function(e) {
     4  //If a is defined, will end up here because
     5  //it is a type error to reference property of undefined
     6 }).catch(ReferenceError, function(e) {
     7  //Will end up here if a wasn't defined at all
     8 }).catch(function(e) {
     9  //Generic catch-the rest, error wasn't TypeError nor
    10  //ReferenceError
    11 });

    4,all()方法

    Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
     
    (1)比如下面代码,两个个异步操作是并行执行的,等到它们都执行完后才会进到 then 里面。同时 all 会把所有异步操作的结果放进一个数组中传给 then。
     1 //切菜
     2 function cutUp(){
     3     console.log('开始切菜。');
     4     var p = new Promise(function(resolve, reject){        //做一些异步操作
     5         setTimeout(function(){
     6             console.log('切菜完毕!');
     7             resolve('切好的菜');
     8         }, 1000);
     9     });
    10     return p;
    11 }
    12  
    13 //烧水
    14 function boil(){
    15     console.log('开始烧水。');
    16     var p = new Promise(function(resolve, reject){        //做一些异步操作
    17         setTimeout(function(){
    18             console.log('烧水完毕!');
    19             resolve('烧好的水');
    20         }, 1000);
    21     });
    22     return p;
    23 }
    24  
    25 Promise
    26 .all([cutUp(), boil()])
    27 .then(function(results){
    28     console.log("准备工作完毕:");
    29     console.log(results);
    30 });

    (2)运行结果如下:

    原文:JS - Promise使用详解2(ES6中的Promise)

    5,race()方法

    race 按字面解释,就是赛跑的意思。race 的用法与 all 一样,只不过 all 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。
    注意:其它没有执行完毕的异步操作仍然会继续执行,而不是停止。
     
    (1)这里我们将上面样例的 all 改成 race
    1 Promise
    2 .race([cutUp(), boil()])
    3 .then(function(results){
    4     console.log("准备工作完毕:");
    5     console.log(results);
    6 });

    原文:JS - Promise使用详解2(ES6中的Promise)
     
    (2)race 使用场景很多。比如我们可以用 race 给某个异步请求设置超时时间,并且在超时后执行相应的操作。
     1 //请求某个图片资源
     2 function requestImg(){
     3     var p = new Promise(function(resolve, reject){
     4     var img = new Image();
     5     img.onload = function(){
     6        resolve(img);
     7     }
     8     img.src = 'xxxxxx';
     9     });
    10     return p;
    11 }
    12  
    13 //延时函数,用于给请求计时
    14 function timeout(){
    15     var p = new Promise(function(resolve, reject){
    16         setTimeout(function(){
    17             reject('图片请求超时');
    18         }, 5000);
    19     });
    20     return p;
    21 }
    22  
    23 Promise
    24 .race([requestImg(), timeout()])
    25 .then(function(results){
    26     console.log(results);
    27 })
    28 .catch(function(reason){
    29     console.log(reason);
    30 });

    上面代码 requestImg 函数异步请求一张图片,timeout 函数是一个延时 5 秒的异步操作。我们将它们一起放在 race 中赛跑。

    • 如果 5 秒内图片请求成功那么便进入 then 方法,执行正常的流程。
    • 如果 5 秒钟图片还未成功返回,那么则进入 catch,报“图片请求超时”的信息。
    原文:JS - Promise使用详解2(ES6中的Promise)

    第三部分:

    JS - Promise使用详解3(jQuery中的Deferred)

    上文我介绍了 ES6 中的 Promise,它完全遵循 Promises/A 规范。而我们熟悉的 jQuery 又有自己的 Promise 实现:Deferred(但其并不是遵循 Promises/A 规范)。本文就讲讲 jQuery 中 Promise 的实现。

     

    一、Deferred对象及其方法

    1,$.Deferred

    • jQuery 用 $.Deferred 实现了 Promise 规范。
    • $.Deferred() 返回一个对象,我们可以称之为 Deferred 对象,上面挂着一些熟悉的方法如:done、fail、then 等。
    • jQuery 就是用这个 Deferred 对象来注册异步操作的回调函数,修改并传递异步操作的状态。
     

    下面我们定义做饭、吃饭、洗碗(cook、eat、wash)这三个方法(这里使用 setTimeout 模拟异步操作)

     1 //做饭
     2 function cook(){
     3     console.log('开始做饭。');
     4     var def = $.Deferred();
     5     //执行异步操作
     6     setTimeout(function(){
     7         console.log('做饭完毕!');
     8         def.resolve('鸡蛋炒饭');
     9     }, 1000);
    10     return def.promise();
    11 }
    12  
    13 //吃饭
    14 function eat(data){
    15     console.log('开始吃饭:' + data);
    16     var def = $.Deferred();
    17     //执行异步操作
    18     setTimeout(function(){
    19         console.log('吃饭完毕!');
    20         def.resolve('一块碗和一双筷子');
    21     }, 1000);
    22     return def.promise();
    23 }
    24  
    25 //洗碗
    26 function wash(data){
    27     console.log('开始洗碗:' + data);
    28     var def = $.Deferred();
    29     //执行异步操作
    30     setTimeout(function(){
    31         console.log('洗碗完毕!');
    32         def.resolve('干净的碗筷');
    33     }, 1000);
    34     return def.promise();
    35 }

    2,then()方法

    通过 Deferred 对象的 then 方法我们可以实现链式调用。
    (1)比如上面样例的三个方法是层层依赖的关系,且下一步的的操作需要使用上一部操作的结果。我们可以这么写:
     1 cook()
     2 .then(function(data){
     3     return eat(data);
     4 })
     5 .then(function(data){
     6     return wash(data);
     7 })
     8 .then(function(data){
     9     console.log(data);
    10 });

    当然也可以简写成如下:

    1 cook()
    2 .then(eat)
    3 .then(wash)
    4 .then(function(data){
    5     console.log(data);
    6 });

    (2)运行结果如下:

    原文:JS - Promise使用详解3(jQuery中的Deferred)

    3,reject()方法

    上面样例我们通过 resolve 方法把 Deferred 对象的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。
    而 reject 方法就是把 Deferred 对象的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)。
     
    (1)下面同样使用一个样例做演示
     1 //做饭
     2 function cook(){
     3     console.log('开始做饭。');
     4     var def = $.Deferred();
     5     //执行异步操作
     6     setTimeout(function(){
     7         console.log('做饭完毕!');
     8         def.reject('烧焦的米饭');
     9     }, 1000);
    10     return def.promise();
    11 }
    12  
    13 //吃饭
    14 function eat(data){
    15     console.log('开始吃饭:' + data);
    16     var def = $.Deferred();
    17     //执行异步操作
    18     setTimeout(function(){
    19         console.log('吃饭完毕!');
    20         def.resolve('一块碗和一双筷子');
    21     }, 1000);
    22     return def.promise();
    23 }
    24  
    25 cook()
    26 .then(eat, function(data){
    27   console.log(data + '没法吃!');
    28 })

    运行结果如下:

    原文:JS - Promise使用详解3(jQuery中的Deferred)
    (2)Promise 规范中,then 方法接受两个参数,分别是执行完成和执行失败的回调。而 jQuery 中进行了增强,还可以接受第三个参数,就是在 pending(进行中)状态时的回调。
    1 deferred.then( doneFilter [, failFilter ] [, progressFilter ] )

    4,done()与fail()方法

    done 和 fail 是 jQuery 增加的两个语法糖方法。分别用来指定执行完成和执行失败的回调。

    比如下面两段代码是等价的:
     1 //then方法
     2 d.then(function(){
     3     console.log('执行完成');
     4 }, function(){
     5     console.log('执行失败');
     6 });
     7  
     8 //done方法、fail方法
     9 d.done(function(){
    10     console.log('执行完成');
    11 })
    12 .fail(function(){
    13     console.log('执行失败');
    14 });

    5,always()方法

    jQuery 的 Deferred 对象上还有一个 always 方法,不论执行完成还是执行失败,always 都会执行,有点类似 ajax 中的 complete。

    1 cook()
    2 .then(eat)
    3 .then(wash)
    4 .always(function(){
    5   console.log('上班去!');
    6 })

    二、与Promises/A规范的差异

    在开头讲到,目前 Promise 事实上的标准是社区提出的 Promises/A 规范,jQuery 的实现并不完全符合 Promises/A,主要表现在对错误的处理不同。

    1,ES6中对错误的处理

    下面代码我们在回调函数中抛出一个错误,Promises/A 规定此时 Promise 实例的状态变为 reject,同时该错误会被下一个 catch 方法指定的回调函数捕获。
    1 cook()
    2 .then(function(data){
    3     throw new Error('米饭被打翻了!');
    4     eat(data);
    5 })
    6 .catch(function(data){
    7     console.log(data);
    8 });

    2,jQuery中对错误的处理

    同样我们在回调函数中抛出一个错误,jQuery 的 Deferred 对象此时不会改变状态,亦不会触发回调函数,该错误一般情况下会被 window.onerror 捕获。换句话说,在 Deferred 对象中,总是必须使用 reject 方法来改变状态。

     1 cook()
     2 .then(function(data){
     3     throw new Error('米饭被打翻了!');
     4     eat(data);
     5 })
     6  
     7 window.onerror = function(msg, url, line) {
     8     console.log("发生错误了:" + msg);
     9     return true; //如果注释掉该语句,浏览器中还是会有错误提示,反之则没有。
    10 }

    原文:JS - Promise使用详解3(jQuery中的Deferred)

    三、$.when方法

    jQuery 中,还有一个 $.when 方法。它与 ES6 中的 all 方法功能一样,并行执行异步操作,在所有的异步操作执行完后才执行回调函数。当有两个地方要注意:
    • $.when 并没有定义在 $.Deferred 中,看名字就知道,$.when 它是一个单独的方法。
    • $.when 与 ES6 的 all 的参数稍有区别,它接受的并不是数组,而是多个 Deferred 对象。
     
    (1)比如下面代码,两个个异步操作是并行执行的,等到它们都执行完后才会进到 then 里面。同时 all 会把所有异步操作的结果传给 then。
     1 //切菜
     2 function cutUp(){
     3     console.log('开始切菜。');
     4     var def = $.Deferred();
     5     //执行异步操作
     6     setTimeout(function(){
     7         console.log('切菜完毕!');
     8         def.resolve('切好的菜');
     9     }, 1000);
    10     return def.promise();
    11 }
    12  
    13 //烧水
    14 function boil(){
    15     console.log('开始烧水。');
    16     var def = $.Deferred();
    17     //执行异步操作
    18     setTimeout(function(){
    19         console.log('烧水完毕!');
    20         def.resolve('烧好的水');
    21     }, 1000);
    22     return def.promise();
    23 }
    24  
    25 $.when(cutUp(), boil())
    26 .then(function(data1, data2){
    27     console.log("准备工作完毕:");
    28     console.log(data1, data2);
    29 });

    四、Ajax函数与Deferred的关系

    jQuery 中我们常常会用到的 ajax, get, post 等 Ajax 函数,其实它们内部都已经实现了 Deferred。这些方法调用后会返回一个受限的 Deferred 对象。既然是 Deferred 对象,那么自然也有上面提到的所有特性。

     

    1,then方法

    比如我们通过链式调用,连续发送多个请求。
     1 req1 = function(){
     2     return $.ajax(/*...*/);
     3 }
     4 req2 = function(){
     5     return $.ajax(/*...*/);
     6 }
     7 req3 = function(){
     8     return $.ajax(/*...*/);
     9 }
    10  
    11 req1().then(req2).then(req3).done(function(){
    12     console.log('请求发送完毕');
    13 });

    2,success、error与complete方法

    success、error、complete是 ajax 提供的语法糖,功能与 Deferred 对象的 done、fail、always 一致。比如下面两段代码功能是一致的:

     1 //使用success、error、complete
     2 $.ajax(/*...*/)
     3 .success(function(){/*...*/})
     4 .error(function(){/*...*/})
     5 .complete(function(){/*...*/})
     6  
     7 //使用done、fail、always
     8 $.ajax(/*...*/)
     9 .done(function(){/*...*/})
    10 .fai(function(){/*...*/})
    11 .always(function(){/*...*/})

    原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_1639.html


    原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_1638.html


    原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_1635.html

  • 相关阅读:
    现代软件工程 第一章 概论 第4题——邓琨
    现代软件工程 第一章 概论 第9题——邓琨
    现代软件工程 第一章 概论 第7题——张星星
    现代软件工程 第一章 概论 第5题——韩婧
    hdu 5821 Ball 贪心(多校)
    hdu 1074 Doing Homework 状压dp
    hdu 1074 Doing Homework 状压dp
    hdu 1069 Monkey and Banana LIS变形
    最长上升子序列的初步学习
    hdu 1024 Max Sum Plus Plus(m段最大子列和)
  • 原文地址:https://www.cnblogs.com/elysian/p/10540911.html
Copyright © 2011-2022 走看看