欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-01-23 20:59:52
...

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--实例讲解
            
    
    博客分类: ES6 javascriptES6HTMLCSSweb,哈哈,我猜肯定没有,这写的啥啊,屁用都没有是吧?肯定是的。但是先别急,上面的代码只是让大家先对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--实例讲解
            
    
    博客分类: ES6 javascriptES6HTMLCSSweb
      现在我们来看下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的兼容性

牛逼哄哄的Promise--实例讲解
            
    
    博客分类: ES6 javascriptES6HTMLCSSweb牛逼哄哄的Promise--实例讲解
            
    
    博客分类: ES6 javascriptES6HTMLCSSweb

  • 牛逼哄哄的Promise--实例讲解
            
    
    博客分类: ES6 javascriptES6HTMLCSSweb
  • 大小: 31.9 KB
  • 牛逼哄哄的Promise--实例讲解
            
    
    博客分类: ES6 javascriptES6HTMLCSSweb
  • 大小: 37 KB