2

这篇文章只解决三个问题。
什么是promise?
promise有什么用?
promise怎么用?

1.什么是promise?

对于ES6来说,就是一个构造函数,可以用new Promise( )来创建Promise实例。

2.promise有什么用?

先想象两个场景:

一,业务中,需要请求后台的两个接口,但是需要第一个返回成功结果之后再请求第二个接口,这是可能会写成下面这个样子,


    var request  = function(){
        var xhr1 = new XMLHttpRequest();
        xhr1.onreadystatechange = function(){
          if(xhr1.readyState !== 4){
            alert('第一步请求失败了,请重试!');
            return;
          }
          if(xhr1.status === 200){
            console.log('第一步请求成功了,我们开始下一步吧!');
            var xhr2 = new XMLHttpRequest();
            xhr2.open("GET", url);
            xhr2.onreadystatechange = function(){
              if(xhr2.readyState !== 4){
                alert('第二步请求失败了,请重试!');
                return;
              }
              if(xhr2.status === 200){
    
                //两次请求成功后做的一些事情....
    
              } else {
                alert('第二步请求失败了,请重试!');
              }
            };
            xhr2.responseType = "json";
            xhr2.setRequestHeader("Accept", "application/json2");
            xhr2.send();
          } else {
            alert('第一步请求失败了,请重试!');
          }
        };
        xhr1.responseType = "json";
        xhr1.setRequestHeader("Accept", "application/json1");
        xhr1.send();
      });
    }
    
    request();

上面代码用xhr对象实现了两次异步请求,代码很长。不过这仅仅是两层回调嵌套,想象一下更多的异步回调依赖有多可怕…

二,我们都知道,nodejs的一大特点就是事件驱动,那就肯定要利用事件的回调函数来处理逻辑,多层的回调嵌套也就在所难免。那么你的代码很可能就会写成这个德行,


    async(1,function(value){
      async(value,function(value){
        async(value,function(value){
          async(value,function(value){
            async(value,function(value){
              async(value,function(value){
                async(value,function(value){
                  async(value,function(value){
                    async(value,final);
                  });
                });
              });
            });
          });
        });
      });
    })

就此你掉进了又长又丑的嵌套地狱。

咋办?

用promise(回答了“promise是干啥的”)。

咋用?

往下看。

3.promise 怎么用?

在学习怎么使用Promise的同时,需要不时接触几个关于它的知识点。能理解最好,理解不了用着用着就理解了。

首先需要了解的是每个Promise实例都有三种状态:

1.进行中状态 pending
2.成功状态 resolved
3.失败状态 rejected

同一时间下Promise实例只能有一种状态,且只能改变一次,改完之后就再也不能变了。

变化的途径只有两种,第一,从pending变为resolved;第二,从pending变为rejected。

接下来就一步步的完成一个promise的使用。

第一步,new一个Promise实例。上代码:


    var promise = new Promise(function(resolve,reject){
      if(/*异步执行成功*/){
        resolve(value)
      } else {
        reject(error)
      }
    });

Promise的构造函数接收一个函数参数,并传入两个参数resolve,reject分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。执行resolve(),会将当前promise对象的状态更改为resolved,执行reject()会将当前promise对象的状态更改为rejected。

这时一个Promise对象已经创建完成,异步脚本的结果也已经被存在这个Promise对象中了,但它是成功是失败?我们怎么读取呢?

第二步,then方法。接着上代码:


    var promise = new Promise(function(resolve,reject){
      if(/*异步执行成功*/){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    promise.then(function(value){
      console.log(value);
    },function(error){
      console.log(error);
    })

每个Promise实例都有一个then方法,它就是处理Promise中存储的异步执行结果的方法。then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

需要特别注意的是then方法的返回值还是一个Promise!这就意味着then方法是支持链式调用的。

第三步,catch方法。
catch方法等同于then(null,function(){}),也就是用来处理rejected状态下Promise数据的。关于catch个人认为记住两点就好。第一,用catch来处理rejected的Promise;第二,用catch来捕获之前所有链式调用中抛出的Error对象,注意是所有,包括Promise构造函数、then方法、链式调用的then方法、以及之前的catch,这些步骤抛出的错误都可以被catch捕获。


    var promise = new Promise(function(resolve,reject){
      if(false){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    promise
      .then(function(value){
        console.log(value);
      })
      .catch(function(reason){
        console.log(error);
      })

上面代码会输出reject(error)传入的error值,也就是catch的第一种用法。


    var promise = new Promise(function(resolve,reject){
      if(true){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    promise
      .then(function(value){
        console.log(value);
        console.log(x);
      })
      .catch(function(reason){
        console.log(reason);
      })

上面代码会将Promise的状态改为resolved,并执行then方法,在then方法中会抛出一个x变量未定义的错误,并由catch方法捕获到并打印出来,这就是catch的第二个用法。

至此实际上我们已经学会了Promise的基础应用。接下来再学习两个比较好用的方法。

第一个,Promise.all方法。

该方法能够并行运行异步方法,并在结果都返回之后进行统一处理,并返回一个新的Promise对象。


    var p = Promise.all([p1, p2, p3]);

上面代码中,Promise.all方法接受一个“数组”作为参数,p1、p2、p3都是Promise对象的实例。

p的状态由p1、p2、p3决定,分成两种情况。

(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。(fulfilled可以理解为resolved)

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。


    var p = Promise.all([p1,p2,p3]);
    
    p.then(function(results){
        console.log(results)
    });

当打开网页时,需要预先加载各种资源如图片、flash以及各种静态文件,所有的都加载完后,我们再进行页面的初始化。这种场景是很适合使用Promise.all的。

第二个,Promise.race方法。

与all方法类似,同样是能够并行运行异步方法,并在结果都返回之后进行统一处理,并返回一个新的Promise对象。


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

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数,也就是说谁执行的快就返回谁的。

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);
    });

到这,关于Promise的三个问题已经解答完了,希望能够为大家学习Promise提供一点帮助。


未末辰降
32 声望0 粉丝

在这做一些学习记录