`

牛逼哄哄的Promise--实例讲解

    博客分类:
  • ES6
阅读更多

Promise已经出现很久了,但鉴于很多初学者还不是很了解也经常有人问到这个,因此就想写一篇文章来讲解一下这方面的知识,本文会尽量用一些简单的实例来说明一些问题,也会尽量做到通俗易懂、简单明了。

参考文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

一、什么是Promise

      Promise是ES6原生提供的一个全局的构造函数,它提供了很多方法供我们使用,它解决了异步操作流程中回掉函数层层嵌套的问题。

      当然,看完上面的解释,相信不会有几个人能真正看懂(大神除外)。少一份套路,多一份真诚,牛逼还是要吹的,但要把握一个度,接下来我们用一个简单的实例来看下:

 

//先实例化一个Promise
var pro=new Promise(function(resolve,reject){
  var name="李";
  resolve(name);//将name传递下去
});
pro.then(function(data){
  //输出成功传来的数据
  console.log(data);
  //先处理数据
  var newData1=data+"可";
  //将处理后的数据传递下去
  return Promise.resolve(newData1);
}).then(function(data){
  //输出成功传来的数据
  console.log(data);
  //先处理数据
  var newData2=data+"可";
  //将处理后的数据传递下去
  return Promise.resolve(newData2);
}).then(function(data){
   //输出最终的数据
   console.log(data);
   //故意抛出一个异常
   throw "Promise不想和你说话,并向你抛出了一个异常!!"
}).catch(function(mes){
   //这里处理被拒绝或发生异常的情况
   //mes参数代表被拒接或异常时携带的信息
   console.log(mes);
});
/* 输出结果如下 */
//李
//李可
//李可可
//Promise不想和你说话,并向你抛出了一个异常!!
       看着是不是很牛逼也很神奇啊吐舌头,哈哈,我猜肯定没有,这写的啥啊,屁用都没有是吧?肯定是的。但是先别急,上面的代码只是让大家先对Promise有个初步的了解,实际应用的时候肯定不会这么简单啊。另外,这里如果还是没搞懂Promise到底是个什么东西也不要着急,毕竟搞对象一直都不是什么容易的事儿,而何况是搞一个这么牛逼的对象是吧。

 

       下面我们就来详细地逐个讲解每个API

二、then和catch方法

      then和catch是Promise.prototype上的两个最常用的方法,下面我们将 从一个最简单的实例开始:

 

var p1 = new Promise(function(resolve, reject) {
  resolve("恭喜你,你赢了");//将来传递给下一层的数据就是里面的参数
});

p1.then(function(data) {
  var num=Math.random();//生成一个随机数
  if(num>0.1){ //用户有90%的可能性会赢
    console.log(data); 
    return;
  }
  throw "厉害了word哥,这都能输!";
}).catch(function(e) {
  console.log(e);//如果输了,则会被嘲讽
});
       1.在这里,我们首先实例化了一个Promise,并将一个函数作为参数传入进去,函数中传入了两个参数resolve和reject,需要注意的是这两个参数都是函数,使用方法是resolve(data)和reject(data),参数data可以是任意数据类型,参数data代表了将要传递下去的数据。
       2.then用来接收上一层传来的数据并处理数据,必要时可以当前层继续向下传递数据(怎样再继续传递数据接下来会讲)。
       3.catch用来捕获异常或捕获拒绝信息,在这里的参数e就代表了抛出的异常信息。
       4.需要注意的是只有Promise对象才有向下传递数据的功能,才有then和catch等方法,所以使用这些牛逼的特性之前必须先new Promise(). 

三、Promise.resolve()和Promise.reject()

       resolve和reject是Promise的两个比较常用的静态方法,关于什么是js的静态方法、原型方法和公共方法大家可以去自行百度。
      前面我们提到了如何实现数据的多级传递,其实我们一开始举的例子里面就早已经将其实现了,但是看起来貌似有点生涩难懂,有些同学可能已经想到了,根据上面第4点所说的,我们直接在then里面再次new一个Promise并返回不就行了吗,那我们现在就来测试一下,看看是否可行:
new Promise(function(resolve,reject){
  resolve(1);
}).then(function(data){
  //返回一个Promise的实例
  return new Promise(function(resolve,reject){
     resolve(data+1);
  })
}).then(function(data){
  //返回一个Promise的实例
  return new Promise(function(resolve,reject){
     resolve(data+1);
  })
}).then(function(data){
  //返回一个Promise的实例
  return new Promise(function(resolve,reject){
     resolve(data+1);
  })
}).then(function(data){
  console.log(data);
})
//4
       哈哈,恭喜某些同学,还真可以,但是我们刚开始就说了 " 它解决了异步操作流程中回掉函数层层嵌套的问题 ",然而我们现在却已经嵌套了三层,虽然嵌套的不是很多,但是我们追求的是完美不是吗?如果还有更复杂的需求那该怎么办,我们岂不是深陷其中而无法自拔吐舌头
      现在我们来看下Promise.resolve()和Promise.reject()是怎么用的:
      Promise.reject()比较简单,它返回一个Promise的实例,并且这个被返回的实例携带着被拒绝的原因(传入的参数),一旦遇到Promise.reject整个流程就会中断,catch也会立即接收到这个原因。当然我们也可以使用throw来代替它,效果是一样的。
      这里我们主要讲解一下Promise.resolve()。这里偷个懒,直接引用MDN的解释,Promise.resolve(value)方法返回一个以给定值解析后的Promise实例。但如果这个值是个thenable(即带有then方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态(指resolved/rejected/pending/settled);否则以该值为成功状态返回promise对象。
      当然,听了这个我们肯定还是云里雾里的啥都没听懂,没关系,我们用实例说话:
Promise.resolve(1)//1
.then(function(data){
  return Promise.resolve(data+1);//2
}).then(function(data){
  return Promise.reject(data+1+"时被拒绝");//3
}).then(function(data){
  return Promise.resolve(data+1);//4
}).then(function(data){
  console.log(data);//结果
}).catch(function(mes){
  console.log(mes);//输出拒绝的"原因"
});
//3时被拒绝
        我们说了,Promise.resolve会返回一个Promise实例,因此上面的代码也就不难理解了,每次都return一个Promise.resolve,而Promise.resolve又会返回一个Promise实例,因此我们可以接着再后面跟"then",从而实现了多级链式调用。
       另外我们也说了,Promise.reject会返回一个Promise的实例,并跳转到catch那一层,catch也会得到被拒绝的原因,因此在3的时候立即中断并被catch捕获。Promise.reject(mes)其实可以用throw mes来代替的,例如这一句代码可以使用throw data+1+"时被拒绝";来代替,
      Promise.resolve()的特殊用法1:除了可以传数值、字符串、boolean、数组和普通对象之外还可以传入另一个Promise对象实例,例如下面的用法:
/*****************1******************/
var original = Promise.resolve(true);
var cast = Promise.resolve(original);
cast.then(function(v) {
  console.log(v); // true
});
/*****************2******************/
var original = new Promise(function(resolve,reject){
  resolve(true);
});
var cast = Promise.resolve(original);
cast.then(function(v) {
  console.log(v); // true
});
 Promise.resolve()的特殊用法2:传入一个特殊的对象(含有then方法),例如下面的代码
/***********************************1************************************/
// Resolve一个thennable对象
var p1 = Promise.resolve({ 
  then: function(resolve, reject) { resolve("fulfilled!"); }
});
console.log(p1 instanceof Promise) // true, 这是一个Promise对象

p1.then(function(v) {
    console.log(v); // 输出"fulfilled!"
  }, function(e) {
    // 不会被调用
});
/**********************************2************************************/
// Thenable在callback之前抛出异常
// Promise rejects
var thenable = { then: function(resolve) {
  throw new TypeError("Throwing");
  resolve("Resolving");
}};

var p2 = Promise.resolve(thenable);
p2.then(function(v) {
  // 不会被调用
}, function(e) {
  console.log(e); // TypeError: Throwing
});
/************************************3*************************************/
// Thenable在callback之后抛出异常
// Promise resolves
var thenable = { then: function(resolve) {
  resolve("Resolving");
  throw new TypeError("Throwing");
}};

var p3 = Promise.resolve(thenable);
p3.then(function(v) {
  console.log(v); // 输出"Resolving"
}, function(e) {
  // 不会被调用
});
      细心的小伙伴可能就会发现其实传入thenable(含有then方法的对象)和传入一个Promise的实例,效果基本上是一样的,其用法和传入一个new Promise(function(resolve,reject){})也非常类似,刚开始我们只需要习惯一种用法即可。

四,Promise的其它写法

/******************1******************/
new Promise(function(res,rej){
  rej("err1");
}).then(function(){

},function(err){
   //捕获到错误
   console.log(err);
})
/*******************2*****************/
Promise.reject("err1").then(function(){

},function(err){
   //捕获到错误
   console.log(err);
})
/*******************3*****************/
Promise.resolve(1)
.then(function(data){
   throw new TypeError("err1")
}).then(function(){

},function(err){
   //捕获到错误
   console.log(err);
});
      总之,then可以有第二个参数,这个参数是一个函数,用来捕获上一层可能会传来的错误。

五、Promise的其它静态方法

      Promise除了上面提到的resolve和reject方法之外,还有两个静态方法
  1. Promise.all()

    请原谅我比较懒,再次复制MDN的解释,Promise.all(iterable) 方法返回一个promise,该promise会等iterable参数内的所有promise都被resolve后被resolve,或以第一个promise被reject的原因而reject 。
    接下来还是先看几个实例吧:
    /*****************1*********************/
    var promise = Promise.resolve(3);
    Promise.all([true, promise])
      .then(values => {
          console.log(values); // [true, 3]
     });
    /*****************2*********************/
    var arr=[1,Promise.reject("err"),Promise.resolve(3)];
    Promise.all(arr)
      .then(values =>{
         console.log(values);//不会执行
    }).catch(err => {
         console.log(err);//err
    });
    /*****************3*********************/
    var arr=[1,Promise.reject("err"),Promise.resolve(3)];
    Promise.all(arr)
      .then(values =>{
         console.log(values);//不会执行
    },err => {
         console.log(err);//err
    });
          总结一下,Promise.all的参数是一个数组,这个数组里面的元素可以是Promise的实例,也可以不是,如果不是(比如传入基本数据类型),则会被自动转换为一个Promise对象,最终会在then的第一个参数里得到一个数组,这个数组里面的每个值对应着当时传入的数组里每个元素(是一个Promise的实例)resolve的值。如果传入数组里面的某一个元素被reject了,那么后面将会立即捕获到这个reject的原因,并且不再理会其它传入的promise是否被resolve。
  2. Promise.race()

    Promise.race(iterable)方法返回一个promise,这个promise在iterable中的任意一个promise被解决或拒绝后,立刻以相同的解决值被解决或以相同的拒绝原因被拒绝。其中iterable是一个存放着若干个Promise的数组。
    通俗来讲,
    iterable内的每一个Promise的实例就像在同一起跑线上的运动员,他们即将进行一场百米赛跑,谁先到达终点谁就会被接纳,而其他运动员则竟被忽略掉。
    看代码:
    /***************************1******************************/
    var p1 = new Promise(function(resolve, reject) { 
        setTimeout(resolve, 500, "一"); 
    });
    var p2 = new Promise(function(resolve, reject) { 
        setTimeout(resolve, 100, "二"); 
    });
    
    Promise.race([p1, p2]).then(function(value) {
      console.log(value); // "二"
      // 两个都解决,但p2更快
    });
    /***************************2******************************/
    var p3 = new Promise(function(resolve, reject) { 
        setTimeout(resolve, 100, "三");
    });
    var p4 = new Promise(function(resolve, reject) { 
        setTimeout(reject, 500, "四"); 
    });
    
    Promise.race([p3, p4]).then(function(value) {
      console.log(value); // "三"
      // p3更快,所以被解决(resolve)了              
    }, function(reason) {
      // 未被执行
    });
    /***************************3******************************/
    var p5 = new Promise(function(resolve, reject) { 
        setTimeout(resolve, 500, "五"); 
    });
    var p6 = new Promise(function(resolve, reject) { 
        setTimeout(reject, 100, "六");
    });
    
    Promise.race([p5, p6]).then(function(value) {
      // 未被执行             
    }, function(reason) {
      console.log(reason); // "六"
      // p6更快,所以被拒绝(reject了)
    });

六、实际应用

       讲了这么多废话,终于到了实际应用了,对于Promise来说,用到ajax当中是最合适不过了,我们可以使用原生的ajax结合Promise来写一个插件,要求可以链式调用

'use strict';

// A-> $http function is implemented in order to follow the standard Adapter pattern
function $http(url){
 
  // A small example of object
  var core = {

    // Method that performs the ajax request
    ajax : function (method, url, args) {

      // Creating a promise
      var promise = new Promise( function (resolve, reject) {

        // Instantiates the XMLHttpRequest
        var client = new XMLHttpRequest();
        var uri = url;

        if (args && (method === 'POST' || method === 'PUT')) {
          uri += '?';
          var argcount = 0;
          for (var key in args) {
            if (args.hasOwnProperty(key)) {
              if (argcount++) {
                uri += '&';
              }
              uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
            }
          }
        }

        client.open(method, uri);
        client.send();

        client.onload = function () {
          if (this.status >= 200 && this.status < 300) {
            // Performs the function "resolve" when this.status is equal to 2xx
            resolve(this.response);
          } else {
            // Performs the function "reject" when this.status is different than 2xx
            reject(this.statusText);
          }
        };
        client.onerror = function () {
          reject(this.statusText);
        };
      });

      // Return the promise
      return promise;
    }
  };

  // Adapter pattern
  return {
    'get' : function(args) {
      return core.ajax('GET', url, args);
    },
    'post' : function(args) {
      return core.ajax('POST', url, args);
    },
    'put' : function(args) {
      return core.ajax('PUT', url, args);
    },
    'delete' : function(args) {
      return core.ajax('DELETE', url, args);
    }
  };
};
// End A

// B-> Here you define its functions and its payload
var mdnAPI = 'https://developer.mozilla.org/en-US/search.json';
var payload = {
  'topic' : 'js',
  'q'     : 'Promise'
};

var callback = {
  success : function(data){
     console.log(1, 'success', JSON.parse(data));
  },
  error : function(data){
     console.log(2, 'error', JSON.parse(data));
  }
};
// End B

// Executes the method call
$http(mdnAPI) 
  .get(payload) 
  .then(callback.success) 
  .catch(callback.error);

// Executes the method call but an alternative way (1) to handle Promise Reject case
$http(mdnAPI) 
  .get(payload) 
  .then(callback.success, callback.error);

// Executes the method call but an alternative way (2) to handle Promise Reject case
$http(mdnAPI) 
  .get(payload) 
  .then(callback.success)
  .then(undefined, callback.error);
$http(mdnAPI) 
  .get(payload) 
  .then(function(data){
      console.log(data);
      //这里可以继续return一个请求的发送
      //例如return $http(mdnAPI).get(payload)
      //内部会return一个Promise的实例,因此下面就可以继续"then"
   }) 
  .catch(callback.error);

 七、Promise的兼容性

  • 大小: 31.9 KB
  • 大小: 37 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics