es6 —— 理解promise对象

Promise对象

所谓Promise,简单来说是一个容器,里面保存着未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。

1. Promise对象的特点
  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中),fufilled(已成功)和rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态。
  2. 一旦状态改变,就不会再变。任何时候得到的结果都是该状态。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
  3. Promise实例新建后就会立即执行
  4. Promise对象中的异步操作会在所有同步操作执行完毕后执行,由于then方法的执行是在异步操作结束后执行,故该方法的执行也是在所有同步操作执行完毕后执行
//2例子
const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test');//由于抛出错误在resolve语句后,故不能被捕获
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) });
// 输出ok
//Promise实例的执行顺序
let data=null;
        let promise=new Promise(function (resolve,reject){
            console.log("start");
            setTimeout(function () {
               console.log("异步操作中");
               resolve("数据")
            },0)
        });
        console.log("外部");
        promise.then(function (res){
            data=res;
            console.log("异步操作结束的data:"+data)
        });
        console.log("end");
        console.log("异步操作未结束的data:"+data);
        //start  
        //外部
        //end
        //异步操作未结束的data:null
        //异步操作中
        //异步操作结束的data:数据
2. Promise对象的优点:
  1. Promise对象可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
  2. Promise对象提供的方法使得异步操作更加容易。
3. Promise对象的缺点
  1. 无法取消Promise,一旦建立Promise对象它就会立即自动执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误不会反应到外部。
  3. 当处于pending状态时无法得知当前是刚开始状态还是即将完成状态。
4. 基本用法

① 由于Promise对象是一个构造函数,故可以通过new Promise(fn)生成Promise实例。
② 回调函数fn的参数resolve函数的作用是将Promise对象的状态从未完成完成成功,在异步操作成功时调用,并将异步操作的结果作为参数传递出去。reject函数的作用是将Promise对象的状态从未完成状态变为失败状态,在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去。
注意:当resolve/reject的参数仍然是一个异步操作时:eg:p2的resolve方法将p1作为参数。此时p1的状态就会传递给p2,也就是说p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变,如果p1的状态是resolve/rejected,那么p2的回调函数会立刻执行。
④ 在③中,如果p1的状态是rejected,而p1是作为p2的resolve方法的参数,此时p2仍会执行rejected的回调函数,而不是执行resolve方法。

function timeout(ms) {
            return new Promise((resolve,reject)=>{
              setTimeout(()=>{
                console.log("我在执行异步操作");
                resolve("执行完毕")//resolve方法将异步操作结果传递出去
            },ms)
            })
 }
        timeout(1000).then((value)=>{
        //在resolve的回调函数中拿到异步操作结果
            setTimeout(()=>{
                console.log(value)
    			},1000)
        });
//异步实现Ajax
const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
      //当异步操作(发请求)执行完毕后将拿到的返回值传递出去
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));//当异步操作(发请求)执行完毕后将拿到的错误信息传递出去
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

//在.then方法中拿到传递出来的值
getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});
//在该例中,虽然过了一秒后p2的状态需要改变为resolve了,但是由于其实参为p1(p1为一个Promise实例,故需要等待p1状态改变。三秒后p1状态变为reject,此时p2需要去执行reject的回调函数)
const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2
  .then(result => console.log(result))
  .catch(error => console.log(error))
// Error: fail
5. Promise.prototype.then()
  1. Promise实例具有then方法,它的作用是为Promise实例添加状态改变时的回调函数。该方法返回的是一个新的Promise实例,故可以采用链式写法,即then方法后面再调用另一个then方法。
  2. 当用链式写法时,无论在resolve回调函数中return 内容还是在rejected回调函数中return 内容都会赋值给Promise实例的promiseValue,只有当Promise实例的promiseValue不为undefined时,才能在执行下一个then方法时的回调函数中取到值。
  3. 无论在回调函数中return的为单纯的内容还是return的Promise实例对象,都可在下次.then的回调函数中取到值。两者的区别是:若return 的为Promise实例对象,则执行下一个then方法时执行哪个回调函数与返回的Promise实例对象的状态有关。
let p3=new Promise(function (resolve, reject) {
            setTimeout(reject, 1000, "错误信息");
        }).then(function (res){
            return res;
        }).catch((value)=>{
            return value;//将值赋值给Promise实例对象的promiseValue属性
        }).then(function (value) {
         return value;//只要promiseValue属性有值便可以获取到
        }).catch(function (reason) {
            console.log(reason);
        })
        console.log(p3)

上面例子中打印出来的p3
在这里插入图片描述

6. Promise.prototype.catch()
  1. 该方法用于指定发生错误时的回调函数。
  2. Promise对象的错误具有"冒泡"性质。会一直向后传递,直到被捕获位置。也就是说错误总会被下一个catch语句捕获。
  3. 一般来说,最好使用catch方法,而不是给then方法添加第二个参数。因为使用catch方法可以捕获到then方法执行的错误
  4. 如果没有使用catch方法指定错误处理的回调函数,Promise抛出的错误不会传递到外层代码,即不会影响外部其他代码的执行。
//下面三个方法等价
//①
const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test
//②
const promise = new Promise(function(resolve, reject) {
  try {
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});
//③
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});
//下面代码中,一共有三个Promise对象,一个由getJSON产生,两个由then产生,三者之一任意一个抛出错误,都会被最后一个catch捕获
getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 处理前面三个Promise产生的错误
});
//注意事项4举例
const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};

someAsyncThing().then(function() {//不会执行
  console.log('everything is great');
});

setTimeout(() => { console.log(123) }, 2000);//仍会输出123,Promise实例内部抛出的错误不会影响外部代码的执行
// Uncaught (in promise) ReferenceError: x is not defined
// 123
7. Promise.prototype.finally()
  1. finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。
  2. finally方法的回调函数不接受任何参数。
8. Promise.prototype.all()
  1. Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例。
  2. Promise.all()方法接受一个数组作为参数,所有参数都是Promise的实例,如果不是,就会先调用Promise.resolve方法将参数转成Promise实例,再进一步处理。该方法的参数可以不是数组但必须具有遍历器接口,且返回的每个成员都为Promise实例。
  3. const p = Promise.all([p1,p2,p3])。在该例子中,只有p1,p2,p3的状态都变为fufilled,p的状态才会编程fufilled.此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数。只要p1,p2,p3之中任意一个被rejected,p的状态就变成rejected。此时第一个被reject的实例的返回值会传递给p的回调函数
 const p1 = new Promise(function (resolve, reject) {
         setTimeout(resolve, 1000, "学生");
         });
  const p2 = new Promise(function (resolve, reject) {
         setTimeout(resolve, 1000, "老师");
         });
         Promise.all([p1,p2]).then((res)=>{
		console.log(res);//["学生","老师"]
		}).catch(function(err){
			console.log(err);//如果p1和p2任意一个状态为rejected,则打印出第一个状态变为rejected的对象内容。
		})
9. Promise.race()
  1. Promise.race()方法同样是将多个Promise实例包装成一个新的Promise实例
  2. 只要参数(即多个Promise实例)中有任意一个实例率先改变状态,该方法返回的Promise实例的状态就会改变,且率先改变状态的实例的返回值会传递给该方法返回的Promise实例的回调函数。
  3. 如果该方法的参数不是Promise实例则会先调用Promise.resolve()方法,将参数先转成Promise实例再进行处理。
10. Promise.resolve()和Promise.reject()方法有待研究
发布了72 篇原创文章 · 获赞 72 · 访问量 6318

猜你喜欢

转载自blog.csdn.net/weixin_43314846/article/details/102782369
今日推荐