ES6学习系列——Promise

Promise 总览:

从中国网民数量急剧增加开始,前端脚本中同步处理就逐渐不够看了,异步处理就被大量使用。这时候,“地狱回调” 就出现了,一层摞一层,让人头大,代码维护的成本变得极大;所以各式各样企图避免“地狱回调”的途径也就应运而生。到了ES6 多个提供异步操作的API 也就出来了,譬如:Promise、Generator(async函数是Generator的语法糖);

Promise 的特点:
  • 一个 Promise 对象就是一个异步操作,这个异步操作存在三种状态,分别是:处理中、成功和失败。Promise 的状态外界无法去影响,只由异步操作的结果决定;
  • 异步操作一旦有了结果,Promise对象的状态就不会再改变,这时候再去给Promise 对象添加回调函数,立马得到的也是这个结果;
  • Promise 对象一旦创建出来就会执行,并且中途不会被打断,而且不设置回调函数的话,Promise 内部出错不会被抛出外部;再者如果它的状态如果是“处理中”,则无法知悉进行到了什么程度;

1、Promise 基本用法

Promise对象是一个构造函数,接收一个函数作为自己的参数,这个函数接受两个参数:resolvereject

上实例:

let promise = new Promise( (resolve,reject) => {
  //doing something
  if (/*成功*/) {
    resolve(value);
  }
  if (/*失败*/) {
    reject(error);
  }
});

Promise对象的实例生成之后,可以用then()方法来指定“成功”和“失败”两种状态的回调函数。then()方法还可以链式调用(因为then()方法返回的是一个Promise对象),

promise.then( 
  (value)=>{
    // 成功,doing something
  }, (error)=>{
    //失败,doing something
}).then((value)=>{}, (error)=>{}).then((value)=>{}, (error)=>{});

第二个回调函数是可选参数;
回调函数的参数必须要与Promise传出的值对应上;
这里来个例子,用 Promise 对象改写 Ajax 操作:

//因为ajax 输出是放在了 xhr.onreadystatechange 阶段,对应 resolve() reject()
let getText = function (url) {
    let promise = new Promise((resolve, reject)=>{

        //创建xhr 对象
        let xhr = new XMLHttpRequest();

        //打开连接
        xhr.open('get', url);

        //发送
        xhr.send(null);

        //接收
        xhr.onreadystatechange = function () {
            if (xhr.readystate == 4) {
                if (xhr.status == 200 && xhr.status < 300 || xhr.status == 304) {
                    resolve(xhr.responseText);

                    //这一句不会不catch()捕抓到,因为Promise 对象的状态已经确定下来
                    throw new Error('test');
                }else{
                    reject(xhr.status);
                }
            }
        }

    });
    return promise;
};

那么来看看都还有哪些方法是值得学一学的:
Promise 实例除了有then() 方法,还有个catch()方法::
catch() 方法接收一个函数作为参数,实际上是 then(null, fn) 的另一种形式。

  • 我们再来看看catch() 方法的一些特点:只要Promise 对象的状态确定下来之前,链式调用中排在它前面的所有错误它都能捕抓到。也就是说,状态确定下来再去捕抓错误是无效的,而Promise 对象的错误具有冒泡性质,会逐步往后传递直到被捕抓;
  • 如果没有写catch()方法,promise 对象抛出的错误不会传递到外面,也就是说虽然出错了,你完全不知道错在何处,什么错误,所以还是老老实实写上这个方法;
  • 另外还有个小建议,最好then()的第二个参数不传,直接上catch() 方法来代替;
getText().then((text)=>{
    //doing something
}).catch((error)=>{

    //getText 和 then()方法中的回调函数出现的错误会被捕抓到
    //注意:getText 中 throw new Error('test');这一句catch()捕抓不到,
    //因为Promise 对象的状态已经确定下来

    console.log('Error:' error);
});

2、再来看看 Promise 对象的其他方法:

Promise.resolve()

这个方法用来将传入的参数,直接转化为Promise 对象;

  • 可以不传参:这时候就会直接返回一个状态为“成功”的 Promise 对象,值得注意的是:这个resolve()方法本轮“事件循环”结束的时候执行,也就是说会比setTimeout()先执行;
  • 参数是一个Promise 对象实例:直接返回这个实例;
  • 参数是一个带有then() 方法的 thenable 对象:这时候会直接将这个对象转化成Promise 对象,然后马上执行 thenable 对象的then()方法;
let thenable = {
    then: function (resolve, reject) {
        resolve('hello world');
    }
};

let promise = Promise.resolve(thenable);

promise.then((val)=>{
    console.log(val); //hello world
});
  • 参数是一个不带 then()方法的对象或者根本就是个值:那么这个方法会直接返回一个状态为“成功”的 Promise对象实例,传入的参数也就是回调函数的参数。
let p = Promise.resolve('hello world');

p.then((string)=>{
    console.log(string); //hello world
});
    -
Promise.all()

这个方法用于将多个Promise 实例包装成一个新的Promise 实例,接收的参数是一个数组(其实只要有Iterator 接口并且返回的所有成员都是Promise 对象实例就行了):
注意:如果p1, p2, p3 还不是Promise 实例,就要手动用 Promise.resolve() 来处理一下;

let newP = Promise.all([p1, p2, p3]);

那么这个newP 实例的状态由什么决定呢?
由p1, p2, p3 三个实例的状态共同决定。
p1, p2, p3 全都为“成功”,则 newP 的状态为“成功”,三者的返回值就会以数组的形式传递给 newP 的回调函数;
p1, p2, p3 只要有一个是“失败”,则newP 的状态就“失败”,第一个状态为“失败”的实例的返回值就会传递给 newP 的回调函数;

Promise.race()

这个方法也是用于将多个Promise 实例包装成一个新的Promise 实例,接收的参数是一个数组

let newP = Promise.race([p1, p2, p3]);

那么返回的实例的状态由什么决定呢?
也是由p1、p2、p3 共同决定;
p1, p2, p3 中只要有一个实例的状态首先确定下来,那么newP 的状态也跟着确定下来,率先改变状态的实例的返回值会传递给 newP 的回调函数;

Promise.reject(reason)

这个方法会返回一个状态为“失败” 的 Promise 实例;
传入的参数会直接作为“失败”的返回值传递给后面的回调函数

let p = Promise.reject(reason);

//equal to 

let p = new Promise((resolve,reject)=>{
    reject(reason);
});
最后,大家如果觉得ES6 提供的API 不够用,那就直接自己造啊:

人有多大胆,地有多大产;

Promise.prototype.xxxx = function () {

};

猜你喜欢

转载自blog.csdn.net/qq_39798135/article/details/82422615