继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

谈谈ECMA6中的Promise

恩言
关注TA
已关注
手记 41
粉丝 316
获赞 3231
谈谈ES6 Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

基本用法
var promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

promise.then(function(value) {
  // success
}).catch(err){
  // failure
};  
概述

Promsie有三种状态,分别是函数执行时的pending,成功时的resolved(fulfilled)以及失败时的rejected。

Promise接受一个回调函数作为参数,并立即执行之。回调参数接收系统传进的两个函数作为参数:resolve和reject,分别用来改变promise的状态为成功及失败。

当我们根据条件调用resolve函数时,由下面promise.then()(的第一个参数回调)接受之,当我们根据条件调用reject函数或抛出错误时,由下面的promise.catch()(或promise.then的第二个参数回调)接收之。可以向这两个函数中传递参数以供then和catch的回调函数接收使用。如果没有resolve函数,then里的内容永远也不会执行。

这样,就可以保证then/catch方法内的回调总在传给Promise构造器的回调函数执行后执行。

Promise对象特点

  1. 对象的状态不受外界影响。
  2. 一旦状态改变,就不会再变

Promise对象缺点:

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
经典例子

ajax

下面是一个通过Promise实现ajax的例子

var getJSON = function(url) {
  var promise = new Promise(function(resolve, reject){
    var client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

    function handler() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});

实现serise

传统方法

我们使用jquery进行ajax请求,最后需要两个ajax请求所获得的数据,传统方法大概会这么做:

   $.get('/test', null, function (data) {
        $.get('/test2', null, function (data2) {
            console.log(data+':'+data2);
        })
    });

Promise解决方案

Promise提供了更好的解决方案

var p1 = new Promise(function (resolve, reject) {
    $.get('/test', null, function (data) {
        console.log(data);
        resolve(data);
    });
});

var p2 = new Promise(function (resolve, reject) {
    $.get('/test2', null, function (data2) {
        console.log(data2);
        resolve(data2);
    })
});

Promise.all([p1, p2]).then(function (arr) {
    console.log(arr[0]+':'+arr[1])
});

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。只有这2个实例的状态都变成fulfilled,或者其中有一个变为rejected,才会调用Promise.all方法后面的回调函数。

于此相对,Promise还提供了静态的race方法,只要有一个实例完成,就执行then里的方法。

实现waterfall

传统方法

我们使用jquery进行ajax请求,当第二个回调函数需要第一个回调函数的返回值作为原始数据时,原始的js代码大概会这么做:

$.get('/test', null, function (data) {
    $.get('/test2', null, function (data2) {
        if(data === 'ok'){
            console.log("It's okay " + data2)
        }else{
            console.log('not okay')
        }
    })
});

两个回调函数还能应付的过来,如果有三个,四个,甚至五个,就难免觉得吃力了。Promise提供了解决方案:

第一种方案

    var pro = new Promise(function (resolve, reject) {

        console.log('pro执行了');

        // 使pro2等待pro更明显
        setTimeout(function () {
            $.get('/test', null, simpleHandler);
        },5000);

        function simpleHandler(data) {
            resolve(data);
        }
    });
    var pro2 = new Promise(function (resolve, reject) {

        console.log('pro2执行了');

        $.get('/test2', null, simpleHandler);

        function simpleHandler(data) {

            console.log('pro2的simpleHandler调用了');

            pro.then(function (data1) {

                console.log('pro返回了!');

                if (data1 === 'ok') {
                    resolve(data);
                } else {
                    reject(data)
                }
            })
        }
    });

    pro2.then(function (data) {
        console.log("It's okay " + data);
    });

依次输出:

 console.log('pro执行了');
 console.log('pro2执行了');
 console.log('pro2的simpleHandler调用了');

等待5s

 console.log('pro返回了!');    
 console.log("It's okay John Doe");

可见,当一个Promise对象的回调里引入另一个Promise对象时,会等待引入的Promise对象执行完毕。

第二种解决方案

var getData = function () {
    return new Promise(function (resolve, reject) {

        console.log('pro执行了');

        // 使pro2等待pro更明显
        setTimeout(function () {
            $.get('/test', null, simpleHandler);
        },5000);

        function simpleHandler(data) {
            resolve(data);
        }
    });
};

var getData2 = function (data1) {
    return new Promise(function (resolve, reject) {

        console.log('pro2执行了');

        $.get('/test2', null, simpleHandler);

        function simpleHandler(data) {

            console.log('pro2的simpleHandler调用了');

                if (data1 === 'ok') {
                    resolve(data);
                } else {
                    reject(data)
                }
        }
    });
};

getData().then(function (data1) {
    return getData2(data1);
}).then(function (data) {
    console.log("It's okay " + data);
});

采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。格式为:

pro1.then(()=>pro2).then(()=>pro3).then(... ...);

括展方法

done

如果promise方法链中的最后一个回调函数发生错误,由于promise的机制,我们无从知道,所以可以为之括展done方法,处于回调链的尾端。

asyncFunc().then(f1) .catch(r1) .then(f2).done();

Promise.prototype.done = function (onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {

    });
};

finally

无论发生错误与否最后都会执行的方法

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => {callback(value)},
    reason => {callback(reason)}
  );
};
打开App,阅读手记
10人推荐
发表评论
随时随地看视频慕课网APP