js-熟知Promise

63 阅读4分钟

一、Promise 是什么?

Promise: 是浏览器新增的内置类,用来管理异步;Promise本身是同步的,是用来管理异步的;传递给Promise的构造函数的回调函数是同步执行的;

let p = new Promise(function (resolve, reject) {
   // 这个函数中处理异步
});

二、Promise实例对象有三种状态

  • pending: 已经初始化,正在处理异步
  • fulfilled: 异步处理成功
  • rejected: 异步处理失败

值得注意的是,Promise的状态一旦发生变更,就会凝固,不会再发生变化;

三、Promise 用法示例

let p2 = new Promise(function (resolve, reject) {
    // 这个函数是同步执行的
   setTimeout(function () {
      resolve('abc') // resolve的实参会传递给then方法的第一个回调函数;
   })
});
  • 在创建Promise示例时传递的回调函数中,存放的是异步执行的任务;
  • resolve 当异步处理成功后执行的,执行的是一个事件池,收集了后面所有的then方法的第一个参数;
  • reject 当异步处理失败后执行的,执行的也是一个事件池,收集了后面所有的then方法的第二个参数;
p2.then(function (data) {
   console.log(1);
   console.log(data);
   // throw 'new err';
   // return 'xyz'
   return new Promise(function (resolve, reject) {
      resolve('就是想解决')
   })
}, function (err) {
   console.log(2);
   console.log(err)
}).then(function (data2) {
   console.log(3);
   console.log(data2)

   // then方法
}, function (err2) {
   console.log(4);
   console.log(err2)
});
  • promise实例对象的第一个then方法的回调函数会根据new Promise()时异步处理的情况来决定
  • 如果是resolve状态,就会执行第一个then方法的第一个回调函数,resolve时传递的实参会传递给第一函数;
  • 如果是reject就会执行第二个,reject时传递的实参会传给第二个函数
  • 但是后面的then方法里面的回调函数执行哪一个取决于执行前面的then方法中函数执行的情况;
  • 如果前一个then中回调没有返回promise实例,无论是第一个还是第二个执行成功了,都会执行后面的then的第一个回调函数。但是前一个then方法中报错了,就会执行第二个;
  • 如果前一个then方法中返回的是一个promise实例,后面的then方法中执行哪个取决于上一个then中返回的promise实例的状态,如果是resolve了,就会执行后面的第一个函数,如果是reject了,就会执行第二个函数;

四、从现实场景看 Promise

  • 首先请求 aside.json,然后从 aside.json 中取得最后一条数据的 id,然后再请求 banner.json。最后把banner.json输出

4.1 使用 jquery

$.ajax({
   url: 'aside.json',
   type: 'get',
   cache: false,
   error (err) {},
   success (res) {
      let [ , ,third] = res;
      $.ajax({
         url: 'banner.json',
         type: 'get',
         cache: false,
         error (err) {},
         success (data) {
            console.log(data);
         }
      })
   }
});

我们发现,上面代码中一层嵌套一层,如果接口多了,那么这种代码组织起来将会十分不便;也是常说的回调地狱

4.2 使用Promise

let p = new Promise(function (resolve, reject) {
   $.ajax({
      url: 'aside.json',
      type: 'GET',
      cache: false,
      error(err) {
         reject(err)
      },
      success (data) {
         resolve(data)
      }
   })
});
p.then(([,, third]) => {
   return new Promise(function (resolve, reject) {
      $.ajax({
         url: 'banner.json',
         type: 'get',
         cache: false,
         error(err) {
            reject(err)
         },
         success (data) {
            resolve(data)
         }
      })
   })
}).then((banner) => {
   console.log(banner)
}).catch((err) => {
   console.log(err)
});
  • 一般情况下,then只放一个成功的回调函数,会把失败的回调函数中放在catch函数中;

4.3 Promise.all() 方法

  • 现在有两个接口,要求必须等着两个接口全部请求完成后才能渲染数据;

类似于上面这种场景就会用到Promise的all方法;

Promise.all([promise实例1, promise实例2])
  • Promise.all 是Promise的静态方法,接受一个有多个promise实例组成的数组,并且返回一个新的Promise实例;
  • 如果数组中所有的Promise实例的状态都变成resolve,那么返回的新的Promise实例的状态才能变成resolve;
  • 如果有一个失败,那么新返回的Promise实例的状态就会变为失败;
  • 同时,会把数组中的promise实例成功的数据组成一个新的数组,传递给后面then方法的第一个函数;

示例:

function queryFn(url) {
   return new Promise((resolve, reject) => {
      $.ajax({
         url: url,
         cache: false,
         error(err) {
            reject(err)
         },
         success (data) {
            resolve(data)
         }
      })
   })
}


Promise.all([queryFn('aside.json'), queryFn('banner.json')]).then((dataArr) => {
   console.log(dataArr)
}).catch((err) => {
   console.log(err)
});

4.4 Promise.race() 方法

前面的一个方法的背景是若干异步都完成后再进行某个操作,还有一种常见场景是当若干异步操作,有一个完成即可进行后续操作了,这种场景下就需要使用 Promise.race() 方法:

Promise.race() 接收一个数组,数组为 Promise 对象,当其中的一个状态发生改变后,就会执行后面的 then 方法;

示例:

let p1 = new Promise((resolve, reject) => setTimeout(resolve, 1000));
let p2 = new Promise((resolve, reject) => setTimeout(resolve, 2000));
let p3 = new Promise((resolve, reject) => setTimeout(resolve, 3000));

Promise.race([p1, p2, p3]).then(res => console.log('Race'))