ES6:理解Promise

1、Promise

Promise以前,在需要多个异步操作的时候,会导致多个回调函数嵌套,导致代码不够直观,就是常说的回调地狱。

aFunc(function(aRes) {
  bFunc(aRes, function(bRes) {
    cFunc(bRes, function(cRes) {
      console.log('得到最终结果: ' + cRes);
    }, callback);
  }, callback);
}, callback);

通常通过 Promise 来解决,Promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果。 什么时候会用到过一段时间?答案是异步操作,异步是指可能比较长时间才有结果的才做,例如网络请求、读取本地文件等。

aFunc().then(function(aRes) {
  return bFunc(aRes);
})
.then(function(bRes) {
  return cFunc(bRes);
})
.then(function(cRes) {
  console.log('得到最终结果: ' + cRes);
})
.catch(callback);
1.1、Promise状态

Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。

1.2、Promise过程
  1. 初始化 Promise 状态(pending)
  2. 立即执行 Promise 中传入的 fn 函数,将 Promise 内部 resolve、reject 函数作为参数传递给 fn ,按事件机制时机处理
  3. 执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)
  4. Promise 里的关键是要保证,then方法传入的参数 onFulfilled 和 onRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。

2、Promise用法

Promise对象是一个构造函数,用来生成Promise实例。

const promise = new Promise(function(resolve, reject) {
    // ... some code

    if (/* 异步操作成功 */){
        resolve(value);
    } else {
        reject(error);
    }
});

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject

  • resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”
  • reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”

3、Promise对象之实例方法

3.1、Promise.prototype.then()

Promise 实例具有 then 方法,也就是说,then方法是定义在原型对象Promise.prototype 上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过, then 方法的第一个参数是 resolved 状态对应的回调函数, 第二个参数(可选) 是 rejected 状态对应的回调函数。

3.2、Promise.prototype.catch()

Promise.prototype.catch() 方法是 .then(null,rejection).then(undefined,rejection) 的别名,用于指定发生错误时的回调函数。

getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // 处理 getJSON 和 前一个回调函数运行时发生的错误
  console.log('发生错误!', error);
});

上面代码中,getJSON()方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。

3.3、Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

getJSON('/posts.json').
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});

上面代码中,不管promise最后的状态,在执行完thencatch指定的回调函数以后,都会执行finally方法指定的回调函数。

3.4、Promise.all()

Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

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

上面代码中,Promise.all()方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

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

3.5、Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

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

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

4、Promise使用场景

  • 将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化
const preloadImage = function (path) {
    return new Promise(function (resolve, reject) {
        const image = new Image();
        image.onload  = resolve;
        image.onerror = reject;
        image.src = path;
    });
};
  • 通过链式操作,将多个渲染数据分别给个then,让其各自处理数据。或当下个异步请求依赖上个请求结果的时候,我们也能够通过链式操作友好解决问题
getData().then(res=>{
    let { bannerList } = res
    console.log(bannerList)
    return res
}).then(res=>{
    let { storeList } = res
    console.log(storeList)
    return res
}).then(res=>{
    let { categoryList } = res
    console.log(categoryList)
    return res
})
  • 通过all()实现多个请求合并在一起,汇总所有请求结果
function initLoad(){
    // loading.show()
    Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{
        console.log(res)
        loading.hide()
    }).catch(err=>{
        console.log(err)
        loading.hide()
    })
}
//数据初始化    
initLoad()
  • 通过race可以设置图片请求超时
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
           resolve(img);
        }
        //img.src = "xx.jpg"; 正确的
        img.src = "xxx.jpggg";  // 错误的
    });
    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);
  });
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容