精华内容
下载资源
问答
  • 为什么要使用PromisePromise优点

    千次阅读 2019-11-11 21:06:30
    promise:启动异步任务=>返回promie对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定) 假设现在有一个名为 createAudioFileAsync() 的函数,如果给出一些配置和两个回调函数,这个函数能异步地...

    1.指定回调函数的方式更加灵活: 
      旧的: 必须在启动异步任务前指定
      promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定) 

    假设现在有一个名为 createAudioFileAsync() 的函数,如果给出一些配置和两个回调函数,这个函数能异步地生成音频文件。一个回调函数是文件成功创建时的回调,另一个则是出现异常时的回调。

    • 不使用Promise,回调函数必须先指定
      // 成功的回调函数
      function successCallback (result) {
          console.log('声音文件创建成功: ' + result)
      }
      // 失败的回调函数
      function failureCallback (error) {
          console.log('声音文件创建失败: ' + error)
      }
      /* 1.1 使用纯回调函数 */
      createAudioFileAsync(audioSettings, successCallback, failureCallback)
    • 使用Promise
      const promise = createAudioFileAsync(audioSettings)
      promise.then(successCallback, failureCallback)
      
      // 可简写为
      createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
      

      可以在异步操作完成后再指定回调函数

    • setTimeout(() => {
          promise.then(successCallback, failureCallback)
      }, 3000)

    2.支持链式调用(将异步操作以同步操作的流程表达出来), 可以解决回调地狱问题
      什么是回调地狱? 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件
      回调地狱的缺点?  不便于阅读 / 不便于异常处理
      解决方案? promise链式调用
      终极解决方案? async/await 

    • 回调地狱 
    • doSomething(function (result) {
          doSomethingElse(result, function (newResult) {
              doThirdThing(newResult, function (finalResult) {
                  console.log('Got the final result: ' + finalResult)
              }, failureCallback)
          }, failureCallback)
      }, failureCallback)
    • 使用promise的链式调用解决回调地狱
    •  doSomething().then(function (result) {
               return doSomethingElse(result)
           })
           .then(function (newResult) {
               return doThirdThing(newResult)
           })
           .then(function (finalResult) {
               console.log('Got the final result: ' + finalResult)
           })
           .catch(failureCallback)
      
      ======
      
      // 箭头函数写法
      doSomething()
          .then(result => doSomethingElse(result))
          .then(newResult => doThirdThing(newResult))
          .then(finalResult => {
              console.log(`Got the final result: ${finalResult}`)
          })
          .catch(failureCallback)
    • async/await: 回调地狱的终极解决方案
    • async function request () {
          try {
              const result = await doSomething()
              const newResult = await doSomethingElse(result)
              const finalResult = await doThirdThing(newResult)
              console.log('Got the final result: ' + finalResult)
          } catch (error) {
              failureCallback(error)
          }
      }

      Promise的缺点

    • 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
    • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
    • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
    const someAsyncThing = function() {
      return new Promise(function(resolve, reject) {
        // 下面一行会报错,因为x没有声明
        resolve(x + 2);
      });
    };
    
    someAsyncThing().then(function() {
      console.log('everything is great');
    });
    
    setTimeout(() => { console.log(123) }, 2000);
    // Uncaught (in promise) ReferenceError: x is not defined
    // 123
    

    上面代码中,someAsyncThing函数产生的 Promise 对象,内部有语法错误。浏览器运行到这一行,会打印出错误提示ReferenceError: x is not defined,但是不会退出进程、终止脚本执行,2 秒之后还是会输出123。这就是说,Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。

    展开全文
  • promise js 有哪些优点

    千次阅读 2014-07-18 18:06:53
    promise 是什么? promises is that a promise represents theresult of an asynchronous operation

    处理异步请求,我们一般这样写

    要求:先取得用户姓名,在根据姓名,取得用户的手机号

    $.get('xxx.com/getUserName',function(data){
        $.get('xxx.com/getMobile',{user:data.userName},function(data){
        });
    });

    多个异步请求在一起,会出现嵌套深的情况,从而带来阅读和维护困难。

    promise 本质上是分离了异步数据获取和业务逻辑,从而让开发人员能专注于一个事物,而不必同时考虑业务和数据

    上面的例子,用promise 可以这样实现

        
        // 第一部分 数据获取和加工阶段
        var getUserName  = function(){
            return new Promise(function(resolve,reject){
                $.get('xxx.com/getUserName',function(data){
                resolve(data);
            });
        };
        var getMobile  = function(userName){
            return new Promise(function(resolve,reject){
                $.get('xxx.com/getUserMobile?user='+userName,function(data){
                    resolve(data);
                });
            });
        }
        // 第二部分 业务逻辑部分
        getUserName().then(function(userName){
            return getMobile(userName);
        }).then(function(mobile){});
     
    }

    数据获取和逻辑分开,加强了代码的可维护性;更细的粒度,有利用代码复用

    promise 是什么?

    promises is that a promise represents the result of an asynchronous operation

    A Promise object represents a value that may not be available yet, but will be resolved at some point in future.


    promiseMeSomething()
    .then(function (value) {
    }, function (reason) {
    });

    promiseMeSomething() 返回一个promise.如果promise 成功返回,则执行then 的第一个函数resolve,如果失败,则执行第二个函数reject

    参考:

    https://github.com/kriskowal/q

    展开全文
  • JavaScript Promise浅析

    2020-08-06 15:39:23
    回调地狱2.Promise使用2.1 reslove2.2 reject2.3 then()2.4 catch()2.4.1 catch()本质2.5 then()第二个函数和catch()区别2.6 finally()2.7 Promise对象本身是同步的2.8 Promise示例2.9 Promise链式调用2.10 Promise...

    JavaScript Promise

    一.概念

    Promise 是ES6对异步编程的一种解决方案,比传统的解决方案(如回调函数和事件)更合理更强大.
    Promise 简单说就是一个容器,里面保存着一个尚未完成且预计在未来完成的异步操作

    Promise是一个构造函数,用来创建一个Promise对象.

    var promise=new Promise();
    

    Promise对象代表一个异步操作,有三种状态

    • pending(进行中)
    • fulfilled(已成功)
    • rejected(已失败)

    改变Promise对象的状态,有两种可能:

    • pending变为fulfilled
    • pending变为rejected

    一旦状态改变,就不会再变,任何时候都可以得到这个结果.
    在这里插入图片描述

    二.Promise对象

    1.回调地狱

    回调函数一个嵌套一个,先执行这个外层的,然后在里层一层一层的去执行

    $.ajax({
        type: "get",
        url: '',
        data: {},
        success: function () {
            $.ajax({
                type: "get",
                url: '',
                data: {},
                success: function () {
                    $.ajax({
                        type: "get",
                        url: '',
                        data: {},
                        success: function () {
                            $.ajax({
                                type: "get",
                                url: '',
                                data: {},
                                success: function () {
                                }
                            });
                        }
                    });
                }
            });
        }
    });
    

    看上面代码,恶不恶心?但是以前我们必须使用这种方式才能得到我们想要的.

    2.Promise使用

    有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,解决了回调地狱!

    const promise = new Promise(function (reslove, reject) {
        if(/*异步操作成功*/){
            reslove('成功');//从pending变为fulfilled
        }
        else{
            reject('失败');//从pending变为rejected
        }
    });
    

    Promise是一个构造函数,接受一个函数作为参数,被称为执行器(executor),该函数的两个参数分别是reslove和reject

    reslove和reject是两个函数,由Javascript引擎提供,不用自己部署

    2.1 reslove

    reslove函数的作用是,将Promise对象的状态从"进行中"变成"成功"(即从pending变为resloved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去

    2.2 reject

    reject 函数的作用是,将Promise对象的状态从"进行中"变为"失败"(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去

    2.3 then()

    Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数

    promise.then(function (value) { 
        //success
    }, function () { 
        //error
    });
    

    Promise实例的then方法:

    • 第一个参数是resolved状态的回调函数
    • 第二个参数(可选) 是rejected状态的回调函数(`也就是说捕获异常还可以通过then方法的第二个参数,但是一般不建议使用,直接使用catch捕获即可)

    最后返回的是一个新的Promise实例

    2.4 catch()

    Promise的实例方法catch:用于指定发生错误时的回调函数

    promise.catch(function (msg) {
        console.log(msg);
    });
    

    在Promise对象中:

    如果该对象状态变为resolved,则会调用then()方法

    如果该对象状态变为rejected,也就是异步操作抛出错误.则会调用catch()方法,另外,在then()里面抛出的异常,也会被catch()捕获

    如果Promise状态已经变成resolved,再抛出异常错误是无效的

    const promise = new Promise(function (reslove, reject
        reslove('成功');
        throw new Error();
    });
    promise.then(function (msg) {
        console.log(msg);
        //success
    }, function (msg) {
        console.log(msg);
        //error
    });
    promise.catch(function (msg) {
        console.log(msg);
    });
    

    上述代码,Promise在resolve语句后面,再抛出异常,不会被捕获,因为Promise的状态一旦改变,就永久保持该状态.

    Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止.所以,有这样的冒泡性质,就不需要在每个Promise对象中单独捕获异常了.

    2.4.1 catch()本质
    Promise.prototype.catch = function(fn){
        return this.then(null,fn);
    }
    

    其实catch的本质还是then()这个方法,只不过进行了封装,一个语法糖而已.

    Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名

    2.5 then()第二个函数和catch()区别
    • then()第二个参数:Promise进入失败状态,或内部执行错误
    • catch()可以捕捉到进入成功状态的函数内部错误
    const promise = new Promise(function (reslove, reject)
        throw new Error();
        reslove('成功');
    });
    promise.then(function (msg) {
        console.log(msg);
        //success
    }, function (msg) {
        console.log("then()第二个函数",msg);
        //error
    }).catch(function (msg) {
        console.log("catch方法",msg);
    });
    

    上面这段代码,我在Promise中抛出一个错误

    then的第二个参数和catch捕获错误信息的时候会就近原则,如果是promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会捕获到。

    const promise = new Promise(function (reslove, reject) {
        throw new Error();
        reslove('成功');
    });
    promise.then(function (msg) {
        console.log(msg);
        //success
    }).catch(function (msg) {
        console.log("catch方法",msg);
    });
    

    如果then()的第二个参数不存在,则会直接被catch()捕获!

    当then()的第二个参数和catch同时存在时,还取决于Promise内部:

    • 如果是reject抛出错误,只有then第二个参数能捕获

    • 如果是resovle抛出成功,那么then的第二个参数不能捕获到,只有catch()才能捕获到!

    const promise = new Promise(function (reslove, reject) {  
        //reslove('成功');
        //reject('失败');
    });
    promise.then(function (msg) {
        throw new Error();
        console.log(msg);
    },function(msg){
        console.log("then方法",msg);
    }).catch(function (msg) {
        console.log("catch方法",msg);
    });
    

    为什么说要推荐使用catch()方法,不推荐使用then()方法的第二个函数呢?因为使用catch()方法可以捕获到前面then()方法里执行中的错误,也更接近同步的写法(try/catch),所以,建议总是使用catch()方法,而不是使用then()的第二个参数

    promise
        .then(function (data) {
            // success
        }, function (err) {
            // error
        });
    promise
        .then(function (data) { 
            // success
        })
        .catch(function (err) {
            // error
        });
    
    2.6 finally()

    finally()方法用于指定不管Promise对象最后状态如何,都会执行的操作 该方法是ES2018引入标准的

    promise.finally(()=>{});
    

    这个finally方法,不管promise的最后状态,在执行完then()catch指定的回调函数以后,都会执行finally方法里指定的回调函数

    server.listen(port)
        .then(function(){
        	//....
    	})
    	.finally(server.stop);
    
    2.7 Promise对象本身是同步的

    Promise是用来管理异步编程的,它本身不是异步的

    var promise = new Promise(function(resolve, reject) {//这里是同步任务
        console.log(1);
        setTimeout(function(){
             console.log(2);
        },0);
        resolve();
    })
    promise.then(function() {//这里是异步任务
        console.log(3);
    })
     console.log(4);  //打印 1,4,3,2
    

    至于为什么为打印1,4,3,2,可以参考事件循环 Event Loop

    2.8 Promise示例

    将所有的异步,变成同步执行!

    需求:

    • 2秒过后吃饭
    • 3秒过后做作业
    • 7秒过后上厕所
    • 1秒过后喝茶
    let status = 0;
    new Promise(function (reslove, reject) {
        setTimeout(() => {
            if (status == 0) {
                console.log("2秒过后吃饭");
                reslove("成功");
            }
            else {
                reject("失败");
            }
        }, 2000);
    }).then(function (msg) {
        console.log("2秒到了" + msg);
        return new Promise(function (reslove, reject) {
            setTimeout(() => {
                console.log("3秒过后做作业");
                reslove("成功");
            }, 3000);
        });
    }).then(function (msg) {
        console.log("3秒到了" + msg);
        return new Promise(function (reslove, reject) {
            setTimeout(() => {
                console.log("7秒过后上厕所");
                reslove("成功");
            }, 7000);
        });
    }).then(function (msg) {
        setTimeout(() => {
            console.log("喝茶");
        }, 1000);
    }).catch(function (msg) {
        console.log("失败了");
    });
    
    //2秒过后吃饭
    //2秒到了成功
    //3秒过后做作业
    //3秒到了成功
    //7秒过后上厕所
    //喝茶
    

    上面的几个过程中,无论哪个对象里面抛出异常,到最后,都可以通过catch()来捕捉,通过这种方式可以将所有 Promise 对象的错误合并到一个函数来处理,这样就解决了每个任务都需要单独处理异常的问题。

    2.9 Promise链式调用

    链式调用,大家可以想到jQuery,jQuery中之所以能够支持链式调用,大家都知道,每个方法最后都会return this,将当前的对象return出去,而咱们的Promise稍微有点不一样

    Promise支持链式调用的原理是:每次对Promise调用then方法之后,都会创建并会返回一个新的Promise对象,可以继续用then方法调用

    2.10 Promise优点缺点
    1.优点

    有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,此外,Promise对象提供统一的接口,使得控制异步操作更加容易

    2.缺点
    • 1.无法取消Promise,一旦新建它,就会立即执行,无法中途取消,

    • 2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部.

    • 3.当处于Pending状态时,无法得知目前进展到哪一个阶段

    针对缺点的第1点,我们可以使用在Promise外层包一层函数,类似于懒加载的原理,需要才加载它,使用它.

    function getPromise(){
        return new Promise(function(resolve,reject){
            resolve('成功');
        });
    }
    
    var promise=getPromise();
    promise.then(function(msg){
        console.log(msg)
    });
    

    三.Promise.all()

    Promise.all()是一个静态方法,属于Promise构造函数的方法

    Promise.all可以将多个Promise实例包装成一个新的Promise实例

    const p = Promise.all([p1, p2, p3]);
    
    • 1.它接收一个数组作为参数
    • 2.数组可以是Promise对象,也可以是其它的值,只有Promise会等待状态改变
    • 3.当所有的子Promise都完成,该Promise完成,返回值是全部值的数组.
    • 4.如果有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果(短路特性).
     function execPromise() {
         var p1 = new Promise(function (res
             reslove('成功p1');
         });
         var p2 = new Promise(function (res
             reslove('成功p2');
         });
         var all = Promise.all([p1, p2]).then(function(data){
             console.log(data);
         }).catch(function (data) {
             console.log(data);
         });
     }
     
     execPromise();//["成功", "成功"]
    

    上述代码:印证了第三点:当所有的子Promise都完成,该Promise完成,返回值是全部值的数组

    function execPromise() {
        var p1 = new Promise(function (reslove, reject) {
            reslove('成功');
        });
        var p2 = new Promise(function (reslove, reject) {
            reslove('成功');
        });
        
        var p3 = new Promise(function (reslove, reject) {
            reject('失败');
        });
        var all = Promise.all([p1, p3, p2]).then(function (data) {
            console.log(data);
        }).catch(function (data) {
            console.log(data);
        });
    }
    
     execPromise();//失败
    

    上述代码:印证了第四点:如果有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果.

    Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示.

    我们需要注意的是:

    Promise.all获取的成功结果的数组顺序和Promise.all接收到的Promise对象数组顺序是一致的,也就是说p1在前面,即使是p1获取到的数据耗时比p2晚,也不会改变!这就带来了一个很大好处:在前端开发请求接口数据过程中,经常会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all,可以完美的解决这个问题

    所以,Promise.all并不是并发执行的!

    1.优点

    可以并行的执行多个请求,等请求全部完成之后在做处理,一般场景有:有多个数据接口我需要等所有的接口返回数据之后,才干后面的事

    2.缺点

    在Promise.all中,只要有一个请求失败,那么就会直接返回失败,后面的无法执行,显然这并不是我们想要的结果,也就是我们常说的短路特性

    既然有缺陷,我们就要弥补,可以通过给子Promise定义catch()的捕捉方法,然后返回resolve,那么Promise.all()的catch()就不会捕捉到~

    function execPromise() {
        var p1 = new Promise(function (reslove, reject) {
            reslove('成功1');
        });
        var p2 = new Promise(function (reslove, reject) {
            reslove('成功2');
        });
        var p3 = new Promise(function (reslove, reject) {
            reject('失败');
        });
        
        Promise.all([p3, p1, p2].map(p => p.catch(function (err) {
            console.log("我是报错",err);
        }))).then(function (res) {
            console.log("都执行了"+res);
        }).catch(function (error) {
            console.log(error);
        });
    }
    execPromise();
    //我是报错 失败
    //都执行了,成功1,成功2
    

    核心内容是map方法,其实就是给每个要执行的Promise对象加个catch()的处理,使用方式是通过map循环.map的每一项都是promise,catch方法返回值会被promise.reslove()包裹,这样传进promise.all的数据都是resolved状态的。

    四.Promise.race()

    Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.race([p1, p2, p3]);
    

    其实,顾名思义,Promise.race就是赛跑的意思,也就是说Promise.race([p1, p2, p3]);里面那个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态,

    原理比较简单,实际开发中,没有遇到什么类似场景,比较少用!

    function execPromise() {
        var p1 = new Promise(function (reslove, reject) {
            reslove('成功1');
        });
        var p2 = new Promise(function (reslove, reject) {
            reslove('成功2');
        });
        var p3 = new Promise(function (reslove, reject) {
            reject('失败');
        });
        var all = Promise.race([p1, p3, p2]).then(function (data) {
            console.log(data);
        }, function (data) {
            console.log(data);
        });
    }
    execPromise(); //结果只会打印成功1
    

    五.Promise.allSettled()

    有时候我们不关系异步操作的结果,只关心这些操作有没有结束,这时,我们就可以考虑使用Promise.allSettled()了,如果没有这个方法,想要确保所有操作都结束.就很麻烦,Promise.all()无法做到这一点

    Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。

    与Promise.all不同的在于.其不会短路,也就是说当Promise全部处理完成之后我们可以拿到每个Promise的状态,而不管其是否处理成功.

    
    function execPromise() {
        var p1 = new Promise(function (reslove, reject) {
            reslove('成功1');
        });
        var p2 = new Promise(function (reslove, reject) {
            reslove('成功2');
        });
        var p3 = new Promise(function (reslove, reject) {
            reject('失败');
        });
        Promise.allSettled([p3, p1, p2]).then(function(data){
            console.log(data);
        });
    }
    execPromise();
    

    打印结果如下:
    在这里插入图片描述

    展开全文
  • promise

    2019-10-08 11:22:11
    JS - Promise使用详解1(基本概念、使用优点) 一、promises相关概念 promises 的概念是由 CommonJS 小组的成员在 Promises/A 规范中提出来的。 1,then()方法介绍 根据 Promise/A 规范,promise 是一个对象,只...

    原文链接http://www.hangge.com/blog/cache/detail_1639.html
    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 装载了由调用返回的值。

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

    2,更好地进行错误捕获
    多重嵌套 callback 除了会造成上面讲的代码缩进问题,更可怕的是可能会造成无法捕获异常或异常捕获不可控。
    (1)比如下面代码我们使用 setTimeout 模拟异步操作,在其中抛出了个异常。但由于异步回调中,回调函数的执行栈与原函数分离开,导致外部无法抓住异常。

    function fetch(callback) {
    setTimeout(() => {
    throw Error(‘请求失败’)
    }, 2000)
    }

    try {
    fetch(() => {
    console.log(‘请求处理’) // 永远不会执行
    })
    } catch (error) {
    console.log(‘触发异常’, error) // 永远不会执行
    }

    // 程序崩溃
    // Uncaught Error: 请求失败

    (2)如果使用 promises 的话,通过 reject 方法把 Promise 的状态置为 rejected,这样我们在 then 中就能捕捉到,然后执行“失败”情况的回调。

    function fetch(callback) {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    reject(‘请求失败’);
    }, 2000)
    })
    }

    fetch()
    .then(
    function(data){
    console.log(‘请求处理’);
    console.log(data);
    },
    function(reason, data){
    console.log(‘触发异常’);
    console.log(reason);
    }
    );

    当然我们在 catch 方法中处理 reject 回调也是可以的。

    function fetch(callback) {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    reject(‘请求失败’);
    }, 2000)
    })
    }

    fetch()
    .then(
    function(data){
    console.log(‘请求处理’);
    console.log(data);
    }
    )
    .catch(function(reason){
    console.log(‘触发异常’);
    console.log(reason);
    });
    第二部分:

    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 模拟异步操作)

    //做饭
    function cook(){
    console.log(‘开始做饭。’);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘做饭完毕!’);
    resolve(‘鸡蛋炒饭’);
    }, 1000);
    });
    return p;
    }

    //吃饭
    function eat(data){
    console.log(‘开始吃饭:’ + data);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘吃饭完毕!’);
    resolve(‘一块碗和一双筷子’);
    }, 2000);
    });
    return p;
    }

    function wash(data){
    console.log(‘开始洗碗:’ + data);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘洗碗完毕!’);
    resolve(‘干净的碗筷’);
    }, 2000);
    });
    return p;
    }

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

    cook()
    .then(function(data){
    return eat(data);
    })
    .then(function(data){
    return wash(data);
    })
    .then(function(data){
    console.log(data);
    });

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

    cook()
    .then(eat)
    .then(wash)
    .then(function(data){
    console.log(data);
    });

    (3)运行结果如下:

    原文:JS - Promise使用详解2(ES6中的Promise)
    2,reject()方法
    上面样例我们通过 resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。
    而 reject 方法就是把 Promise 的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)。

    (1)下面同样使用一个样例做演示

    //做饭
    function cook(){
    console.log(‘开始做饭。’);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘做饭失败!’);
    reject(‘烧焦的米饭’);
    }, 1000);
    });
    return p;
    }

    //吃饭
    function eat(data){
    console.log(‘开始吃饭:’ + data);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘吃饭完毕!’);
    resolve(‘一块碗和一双筷子’);
    }, 2000);
    });
    return p;
    }

    cook()
    .then(eat, function(data){
    console.log(data + ‘没法吃!’);
    })
    运行结果如下:

    原文:JS - Promise使用详解2(ES6中的Promise)
    (2)如果我们只要处理失败的情况可以使用 then(null, …),或是使用接下来要讲的 catch 方法。

    cook()
    .then(null, function(data){
    console.log(data + ‘没法吃!’);
    })

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

    cook()
    .then(eat)
    .catch(function(data){
    console.log(data + ‘没法吃!’);
    });

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

    //做饭
    function cook(){
    console.log(‘开始做饭。’);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘做饭完毕!’);
    resolve(‘鸡蛋炒饭’);
    }, 1000);
    });
    return p;
    }

    //吃饭
    function eat(data){
    console.log(‘开始吃饭:’ + data);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘吃饭完毕!’);
    resolve(‘一块碗和一双筷子’);
    }, 2000);
    });
    return p;
    }

    cook()
    .then(function(data){
    throw new Error(‘米饭被打翻了!’);
    eat(data);
    })
    .catch(function(data){
    console.log(data);
    });
    运行结果如下:

    原文:JS - Promise使用详解2(ES6中的Promise)
    这种错误的捕获是非常有用的,因为它能够帮助我们在开发中识别代码错误。比如,在一个 then() 方法内部的任意地方,我们做了一个 JSON.parse() 操作,如果 JSON 参数不合法那么它就会抛出一个同步错误。用回调的话该错误就会被吞噬掉,但是用 promises 我们可以轻松的在 catch() 方法里处理掉该错误。

    (3)还可以添加多个 catch,实现更加精准的异常捕获。

    somePromise.then(function() {
    return a();
    }).catch(TypeError, function(e) {
    //If a is defined, will end up here because
    //it is a type error to reference property of undefined
    }).catch(ReferenceError, function(e) {
    //Will end up here if a wasn’t defined at all
    }).catch(function(e) {
    //Generic catch-the rest, error wasn’t TypeError nor
    //ReferenceError
    });

    4,all()方法
    Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。

    (1)比如下面代码,两个个异步操作是并行执行的,等到它们都执行完后才会进到 then 里面。同时 all 会把所有异步操作的结果放进一个数组中传给 then。

    //切菜
    function cutUp(){
    console.log(‘开始切菜。’);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘切菜完毕!’);
    resolve(‘切好的菜’);
    }, 1000);
    });
    return p;
    }

    //烧水
    function boil(){
    console.log(‘开始烧水。’);
    var p = new Promise(function(resolve, reject){ //做一些异步操作
    setTimeout(function(){
    console.log(‘烧水完毕!’);
    resolve(‘烧好的水’);
    }, 1000);
    });
    return p;
    }

    Promise
    .all([cutUp(), boil()])
    .then(function(results){
    console.log(“准备工作完毕:”);
    console.log(results);
    });

    (2)运行结果如下:

    原文:JS - Promise使用详解2(ES6中的Promise)
    5,race()方法
    race 按字面解释,就是赛跑的意思。race 的用法与 all 一样,只不过 all 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。
    注意:其它没有执行完毕的异步操作仍然会继续执行,而不是停止。

    (1)这里我们将上面样例的 all 改成 race

    Promise
    .race([cutUp(), boil()])
    .then(function(results){
    console.log(“准备工作完毕:”);
    console.log(results);
    });
    原文:JS - Promise使用详解2(ES6中的Promise)

    (2)race 使用场景很多。比如我们可以用 race 给某个异步请求设置超时时间,并且在超时后执行相应的操作。

    //请求某个图片资源
    function requestImg(){
    var p = new Promise(function(resolve, reject){
    var img = new Image();
    img.onload = function(){
    resolve(img);
    }
    img.src = ‘xxxxxx’;
    });
    return p;
    }

    //延时函数,用于给请求计时
    function timeout(){
    var p = new Promise(function(resolve, reject){
    setTimeout(function(){
    reject(‘图片请求超时’);
    }, 5000);
    });
    return p;
    }

    Promise
    .race([requestImg(), timeout()])
    .then(function(results){
    console.log(results);
    })
    .catch(function(reason){
    console.log(reason);
    });
    上面代码 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 模拟异步操作)

    //做饭
    function cook(){
    console.log(‘开始做饭。’);
    var def = $.Deferred();
    //执行异步操作
    setTimeout(function(){
    console.log(‘做饭完毕!’);
    def.resolve(‘鸡蛋炒饭’);
    }, 1000);
    return def.promise();
    }

    //吃饭
    function eat(data){
    console.log(‘开始吃饭:’ + data);
    var def = $.Deferred();
    //执行异步操作
    setTimeout(function(){
    console.log(‘吃饭完毕!’);
    def.resolve(‘一块碗和一双筷子’);
    }, 1000);
    return def.promise();
    }

    //洗碗
    function wash(data){
    console.log(‘开始洗碗:’ + data);
    var def = $.Deferred();
    //执行异步操作
    setTimeout(function(){
    console.log(‘洗碗完毕!’);
    def.resolve(‘干净的碗筷’);
    }, 1000);
    return def.promise();
    }

    2,then()方法
    通过 Deferred 对象的 then 方法我们可以实现链式调用。
    (1)比如上面样例的三个方法是层层依赖的关系,且下一步的的操作需要使用上一部操作的结果。我们可以这么写:

    cook()
    .then(function(data){
    return eat(data);
    })
    .then(function(data){
    return wash(data);
    })
    .then(function(data){
    console.log(data);
    });
    当然也可以简写成如下:

    cook()
    .then(eat)
    .then(wash)
    .then(function(data){
    console.log(data);
    });

    (2)运行结果如下:

    原文:JS - Promise使用详解3(jQuery中的Deferred)
    3,reject()方法
    上面样例我们通过 resolve 方法把 Deferred 对象的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。
    而 reject 方法就是把 Deferred 对象的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)。

    (1)下面同样使用一个样例做演示

    //做饭
    function cook(){
    console.log(‘开始做饭。’);
    var def = $.Deferred();
    //执行异步操作
    setTimeout(function(){
    console.log(‘做饭完毕!’);
    def.reject(‘烧焦的米饭’);
    }, 1000);
    return def.promise();
    }

    //吃饭
    function eat(data){
    console.log(‘开始吃饭:’ + data);
    var def = $.Deferred();
    //执行异步操作
    setTimeout(function(){
    console.log(‘吃饭完毕!’);
    def.resolve(‘一块碗和一双筷子’);
    }, 1000);
    return def.promise();
    }

    cook()
    .then(eat, function(data){
    console.log(data + ‘没法吃!’);
    })
    运行结果如下:

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

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

    比如下面两段代码是等价的:

    //then方法
    d.then(function(){
    console.log(‘执行完成’);
    }, function(){
    console.log(‘执行失败’);
    });

    //done方法、fail方法
    d.done(function(){
    console.log(‘执行完成’);
    })
    .fail(function(){
    console.log(‘执行失败’);
    });

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

    cook()
    .then(eat)
    .then(wash)
    .always(function(){
    console.log(‘上班去!’);
    })

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

    1,ES6中对错误的处理
    下面代码我们在回调函数中抛出一个错误,Promises/A 规定此时 Promise 实例的状态变为 reject,同时该错误会被下一个 catch 方法指定的回调函数捕获。

    cook()
    .then(function(data){
    throw new Error(‘米饭被打翻了!’);
    eat(data);
    })
    .catch(function(data){
    console.log(data);
    });

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

    cook()
    .then(function(data){
    throw new Error(‘米饭被打翻了!’);
    eat(data);
    })

    window.onerror = function(msg, url, line) {
    console.log(“发生错误了:” + msg);
    return true; //如果注释掉该语句,浏览器中还是会有错误提示,反之则没有。
    }
    原文:JS - Promise使用详解3(jQuery中的Deferred)

    三、$.when方法
    jQuery 中,还有一个 $.when 方法。它与 ES6 中的 all 方法功能一样,并行执行异步操作,在所有的异步操作执行完后才执行回调函数。当有两个地方要注意:
    $.when 并没有定义在 . D e f e r r e d 中 , 看 名 字 就 知 道 , .Deferred 中,看名字就知道, .Deferred.when 它是一个单独的方法。
    $.when 与 ES6 的 all 的参数稍有区别,它接受的并不是数组,而是多个 Deferred 对象。

    (1)比如下面代码,两个个异步操作是并行执行的,等到它们都执行完后才会进到 then 里面。同时 all 会把所有异步操作的结果传给 then。

    //切菜
    function cutUp(){
    console.log(‘开始切菜。’);
    var def = $.Deferred();
    //执行异步操作
    setTimeout(function(){
    console.log(‘切菜完毕!’);
    def.resolve(‘切好的菜’);
    }, 1000);
    return def.promise();
    }

    //烧水
    function boil(){
    console.log(‘开始烧水。’);
    var def = $.Deferred();
    //执行异步操作
    setTimeout(function(){
    console.log(‘烧水完毕!’);
    def.resolve(‘烧好的水’);
    }, 1000);
    return def.promise();
    }

    $.when(cutUp(), boil())
    .then(function(data1, data2){
    console.log(“准备工作完毕:”);
    console.log(data1, data2);
    });

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

    1,then方法
    比如我们通过链式调用,连续发送多个请求。

    req1 = function(){
    return $.ajax(//);
    }
    req2 = function(){
    return $.ajax(//);
    }
    req3 = function(){
    return $.ajax(//);
    }

    req1().then(req2).then(req3).done(function(){
    console.log(‘请求发送完毕’);
    });

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

    //使用success、error、complete
    $.ajax(//)
    .success(function(){//})
    .error(function(){//})
    .complete(function(){//})

    //使用done、fail、always
    $.ajax(//)
    .done(function(){//})
    .fai(function(){//})
    .always(function(){//})

    展开全文
  • Promise

    2020-08-04 18:10:14
    Promise相对于纯回调函数的优点: 1.方式更加灵活: 旧的纯回调函数必须在异步任务启动前指定回调函数 asyncFunction(settings, successCallBack, failCallBack); Promise在返回Promise对象之后或再经过一段时间...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,191
精华内容 4,876
关键字:

promise优点