promise

头像
Clli
    阅读 8 分钟
    1

    问题:

     一个页面 进入页面会有四个接口请求,要求每个请求都带loading,请求完 loading消失。从页面加载速度,和资源浪费 两方面考虑, 用promise 如何实现

    内容:

    1、promise的含义
    2、基本用法
    3、 prototype.then()
    4、prototype.catch()
    5、Promise.reslove()
    6、Promise.reject()
    7、prototype.finally()
    8、Promise.all()
    9、Promise.any()
    10、Promise.allSettled()
    11、Promise.race()
    12、Promise.try()
    13、应用
    一、promise的含义

    自我定义:处理异步的一个手段,将异步操作改成同步操作的形式 

    标准定义:

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

    特点: 

    三种状态: reslove(成功)  reject(失败) pending(初始化/进行中)

    状态只能是从pending ===>reslove/reject    并且状态已经改变,不会在改了

    eg.

    const promise = new Promise(function(resolve, reject) {
    
      resolve('ok');
    
      reject('no');
    
    });
    
    promise
    
        .then(function(value) { console.log(value) })
    
        .catch(function(error) { console.log(error) });
    
    // ok

    缺点:

    1、无法被取消,创建就会立即执行,中途不会取消,

    let promise = new Promise(function(resolve, reject) {
    
      console.log('Promise');
    
      resolve();
    
    });
    
    
    promise.then(function() {
    
      console.log('resolved.');
      
    });
    
    console.log('Hi!');
    
    // Promise
    
    // Hi!
    
    // resolved  

    2、如果不设置回调函数Promise内部的错误不会反应到外部,可以把内部错误给‘吃掉’

    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);
    
    // Uncaught (in promise) ReferenceError: x is not defined
    
    // 123

    3、 当处于pending状态时,无法得知目前进展到哪一个阶段

    二、基本用法

    function fn() {
    
      return new Promise((resolve, reject) => {
    
        if(true) {
    
          resolve(true)
    
        }else {
    
          reject(false)
    
        }
    
      })
    
    }
    
    fn.then(res => {
    
      //成功
    
    }).catch(oError => {
    
      //失败
    
    })
    
    
    fn.then(res => {
    
      //成功
    
    },oError => {
    
      //失败
    
    })
    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
    三、prototype.then()

    Promise 实例添加状态改变时的回调函数,then(reslove,reject),两个参数,第二个可以不写

    四、prototype.catch()

    用于指定发生错误时的回调函数 catch其实是对应then的第二个参数 then('',catch)

    // 写法一
    
    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 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

    function p1() {
    
      return new Promise((resolve, reject) => {
    
        resolve(111)
    
      })
    
    }
    
    function p2() {
    
      return new Promise((resolve, reject) => {
    
        reject(2222222)
    
      })
    
    }
    
    function p3() {
    
      return new Promise((resolve, reject) => {
    
        resolve(3333)
    
      })
    
    }
    
    p1().then(p2).then(p3).then(res => {
    
      console.log(res);
    
    }).catch(e => {
    
      console.log(e)
    
    })
    
    p1().then(function () {
    
      p2().then(function () {
    
        p3().then(res=> {
    
          console.log(res);
    
        }).catch(e=> {
    
          console.log(e);
    
        })
    
      }).catch(e=> {
    
        console.log(e);
    
      })
    
    })
    五、Promise.reslove()  转化成Promise对象

    Promise.resolve('foo')
    
    // 等价于
    
    new Promise(resolve => resolve('foo'))

        1、如果参数是Promise对象 ,不做任何处理  直接返回

    2、参数是具有then方法的对象。

    let thenable = {
    
      then: function(resolve, reject) {
    
        resolve(42);
    
      }};

    会将他转化成Promise对象,然后立即执行对象的then方法

    let thenable = {
    
      then: function(resolve, reject) {
    
        resolve(42);
    
      }
    
    };
    
    let p1 = Promise.resolve(thenable);
    
    p1.then(function(value) {
    
      console.log(value);  // 42
    
    });
    let thenable = {
    
      then: function(resolve, reject) {
    
        reject(42);
    
      }
    
    };
    
    let p1 = Promise.resolve(thenable);
    
    console.log(p1);
    
    p1.then(function(value) {
    
      console.log(value);
    
    }, oError=> {
    
      console.log(oError); //42
    
    });

    3、参数没有then方法或者不是对象   ------  返回一个新的 Promise 对象,状态为resolved

    let p = Promise.resolve('hello')
    
    console.log(p);
    
    p.then(res=>{
    
      console.log(res);
    
    })

    4、参数为空  ----  会直接返回一个Promise对象

    const p = Promise.resolve();
    
    p.then(function () {
    
      // ...
    
    });

    then是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时

    setTimeout(function () {
    
      console.log('three');
      
    }, 0)
    
    Promise.resolve().then(function () {
    
      console.log('two');
      
    });
    
    console.log('one');
    
    // one
    
    // two
    
    // three
    六、Promise.reject()

    方法也会返回一个新的 Promise 实例,该实例的状态为rejected。 原封不动地作为reject的理由,变成后续方法的参数

    const thenable = {
    
      then(resolve, reject) {
    
        reject('出错了');
    
      }};
    
    
    Promise.reject(thenable).catch(e => {
    
      console.log(e === thenable)})
    
    // true
    七、prototype.finally()

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

    promise
    
    .finally(() => {
    
      // 语句
    
    });
    
    // 等同于
    
    promise
    
    .then(
    
      result => {
    
        // 语句
    
        return result;
    
      },
    
      error => {
    
        // 语句
    
        throw error;
    
      });
    Promise.prototype.finally = function (callback) {
    
      let P = this.constructor;
    
      return this.then(
    
        value  => P.resolve(callback()).then(() => value),
    
        reason => P.resolve(callback()).then(() => { throw reason })
    
      );};
    八、Promise.all()  都成功才成功,一个失败就失败

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

    如果参数不是Promise对象会先变成Promise对象  通过Promise.reslove()方法

    Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口  可循环的数据  for ... of  

    Array  
    
    Object  
    
    Map([['one', 1], ['two', 2]]) 
    
    Set([1, 1, 2, 2, 1])
    let p1 = 'hello'
    
    let p2 = new Promise((resolve, reject) => {
    
      setTimeout(function () {
    
        resolve(222)
    
      },3100)
    
    })
    
    let p3 = new Promise((resolve, reject) => {
    
      setTimeout(function () {
    
        resolve(333)
    
      },3000)
    
    })
    
    Promise.all(\[p1,p2,p3\]).then(res=> {
    
      console.log(res);
    
    },error => {
    
      console.log(error);
    
    })
    
    //[
        //'hello',
    
        //222,
    
       // 333
    //]
    
    //222

    注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。

    那如果自己定义了then呢?

    const p1 = new Promise((resolve, reject) => {
    
      resolve('hello');})
    
    .then(result => result)
    
    .catch(e => e);
    
    
    const p2 = new Promise((resolve, reject) => {
    
      throw new Error('报错了');})
    
    .then(result => result)
    
    .catch(e => e);
    
    
    Promise.all([p1, p2])
    
    .then(result => console.log(result))
    
    .catch(e => console.log(e));
    
    // ["hello", Error: 报错了]

    上面代码中,p1会resolved,p2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。

    九、Promise.any()  一个成功就成功,都失败才失败   

    目前还是提案中

    npm install p-any
    let p2 = new Promise((resolve, reject) => {
    
      reject(222)
      
    })
    
    let p3 = new Promise((resolve, reject) => {
    
      reject(333)
      
    })
    
    py([p2,p3]).then(res=> {
    
      console.log(res);
      
    },error => {
    
      console.dir(error);
    })

    any.png

    十、Promise.allSettled()  都结束才结束

    如果参数不是Promise对象会先变成Promise对象  通过Promise.reslove()方法

    let p1 = new Promise((resolve, reject) => {
    
      resolve(111)
    
    })
    
    let p2 = new Promise((resolve, reject) => {
    
      reject(222)
    
    })
    
    let p3 = new Promise((resolve, reject) => {
    
      reject(333)
    
    }
    
    Promise.allSettled([p1,p2,p3]).then(res => {
    
      console.log(res);
    
    })

    allSettled.png
    返回的是数组,reslove的是status和value,  失败的是status和reason

    res.filter(ites=>ites.status==='fulfilled') 
    
    [
        {
            status: 'fulfilled',
            value: 111
        }
    ]

    应用场景 : 不关心异步操作的结果,只关心这些操作有没有结束。例如loading结束

    十一、Promise.race()  一个变就会变

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

    如果参数不是Promise对象会先变成Promise对象  通过Promise.reslove()方法

    那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

    let p1 = 'hello'
    
    let p2 = new Promise((resolve, reject) => {
    
      setTimeout(function () {
    
        resolve(222)
    
      },3100)
    
    })
    
    let p3 = new Promise((resolve, reject) => {
    
      setTimeout(function () {
    
        reject(333)
    
      },3000)
    
    })
    
    Promise.race(\[p1,p2,p3\]).then(res=> {
    
      console.log(res);
    
    },error => {
    
      console.log(error);
    
    })

    应用场景: timeout

    const p = Promise.race([
    
      axios('/api'),
    
      new Promise(function (resolve, reject) {
    
        setTimeout(() => reject(new Error('request ****timeout')), 5000)
    
      })]);
    十二、Promise.try()  

    不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。

    不必理会函数本身是同步的还是异步的,

    let p4 =() => new Promise((resolve, reject) => {
    
      reject(222)
    
    })
    
    let aa = () => {
    
      console.log(111111)
    
    }
    
    pt(p4).then(res=> {
    
      console.log(res);
    
    }).catch(error=> {
    
      console.log(error);
    
    })
    
    console.log('test')
    
      
    // test
    
    // 222
    十三、 应用

    图片加载
      function loadImageAsync(url) {
        return new Promise(function(resolve, reject) {
          const image = new Image();
          image.onload = function() {
            resolve(image);
          };
    
          image.onerror = function() {
            reject(new Error('Could not load image at ' + url));
          };
          image.src = url;
          image.qu
        });
      }
      loadImageAsync('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1587961648939&di=40166f91316c3f23c2964ee73ee72946&imgtype=0&src=http%3A%2F%2Fimg.kutoo8.com%2Fupload%2Fimage%2F95351812%2F001%2520%283%29_960x540.jpg')
      .then(img => {
        console.log(img);
        document.querySelector('body').appendChild(img)
      })

    最后 说一下前边提到的问题,我现在的是用的常规的方法,就是一个请求完在请求第二个, 当第一个失败了,我就不去请求了,如果有更好的办法,请留言,在这里抱拳了


    剧终
    谢幕

    Clli
    46 声望1 粉丝

    一个来自农村的小伙子