美文网首页
Promise教程及用法

Promise教程及用法

作者: 鹏多多 | 来源:发表于2021-08-16 11:11 被阅读0次

    1,介绍

    Promise是异步编程的一种解决方案,比回调函数和事件更合理且更强大。可以理解为一个容器,里面保存着某个未来才会结束的事件的结果。

    2,特点

    • Promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),该状态不受外界影响。

    • 状态可以从pending变为fulfilled,或者从pending变为rejected。一旦状态改变,就不会再变。

    3,缺点

    • Promise一旦新建它就会立即执行,无法中途取消

    • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部

    • 当处于pending状态时,无法得知目前的进展是刚开始还是即将完成

    4,基本用法

    ES6规定,Promise对象是一个构造函数,用来生成Promise实例,它接受一个函数作为参数,该函数的两个函数参数分别是resolverejectresolve的作用是将Promise对象的状态从未完成变为成功,reject函数的作用是将Promise对象的状态从未完成变为失败。

    const promise = new Promise(function(resolve, reject) {
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    

    5,then

    Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数(一般都只用第一个参数,reject状态交由catch专门处理)

    function timeout(ms) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve('done'), ms);
      });
    }
    
    timeout(100).then(
        // resolve回调
      (value) => { console.log(value) },
      // reject回调
      (error) => { console.log(error) }
    );
    

    Promise新建后就会立即执行,但是then方法是微任务,将在当前脚本所有同步任务执行完才会执行。如下代码:首先输出的是A。然后才是then方法指定的回调函数,所以B最后输出。

    let promise = new Promise(function(resolve, reject) {
      console.log('A');
      resolve();
    });
    
    promise.then(function() {
      console.log('B');
    });
    
    console.log('C');
    
    // 输出顺序 A C B
    

    then方法返回的是一个新的Promise实例(不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

    getJSON("/api")
    .then(post => getJSON(post.commentURL))
    .then(comments => console.log("resolve", comments))
    

    如下代码:p2操作的结果是返回p1的异步操作结果。这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。

    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))
    

    6,catch

    then一样,catch()方法返回的也是一个Promise对象,因此后面还可以接着调用then()方法,如果没有报错,则会跳过catch()方法。此时,要是后面的then()方法里面报错,就与前面的catch()无关了

    const someAsyncThing = function() {
      return new Promise(function(resolve, reject) {
        // 下面一行会报错,因为x没有声明
        resolve(x + 2);
      });
    };
    
    someAsyncThing()
    .catch(function(error) {
      console.log('oh no', error);
    })
    .then(function() {
      console.log('carry on');
    });
    

    7,finally

    finally()方法是ES2018引入标准的。用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法总是会返回原来的值,并且不接受任何参数,这意味着没有办法知道前面的 Promise状态到底是fulfilled还是rejected。

    promise
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});
    

    8,all()

    Promise.all()方法用于将多个Promise实例,包装成一个新的 Promise实例。该方法接受一个数组作为参数(可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例)

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

    如上代码,p1p2p3都是Promise实例,如果不是,就会先调用Promise.resolve(),将参数转为Promise实例,再进一步处理。

    注意:

    • 只有数组里所有的Promise都完成,Promise.all()才会完成

    • 如果数组里的Promise有一个失败,Promise.all()就会失败,第一个失败的Promise实例会传递给Promise.all()的回调

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

    9,race()

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

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

    race的意思是速度,和字面意思一样,谁快,就取谁作为结果。上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。率先改变的Promise实例的返回值,就传递给p的回调函数。

    10,allSettled()

    Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个Promise对象,并返回一个新的Promise数组集合。

    注意:只有等到参数数组的所有Promise对象都发生状态变更(不管是fulfilled还是rejected),返回的Promise对象才会发生状态变更。

    const p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
      }, 1000)
    })
    const p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('no')
      }, 2000)
    })
    const p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
      }, 3000)
    })
    
    const all = Promise.allSettled([p1, p2, p3])
    .then(res => {
      console.log(res)
    })
    

    11,any()

    Promise.any()方法接受一个数组作为参数,数组的每个成员都是一个Promise对象,并返回一个新的Promise数组集合。

    注意:只要参数数组的Promise对象有一个变成fulfilled状态,Promise.any()就会变成fulfilled状态。只有参数数组里所有Promise对象都变成rejected状态,Promise.any()才会变成rejected状态。

    const all = Promise.any([p1, p2, p3])
    .then(res => {
        console.log(res)
    })
    .catch(err => {
        console.log(err)
    })
    

    12,现有对象转为Promise对象

    有时需要将现有对象转为Promise对象,Promise.resolve()Promise.reject()就起到这个作用。

    onst p = Promise.resolve('Hello');
    
    p.then(function (s) {
      console.log(s)
    });
    // 输出 Hello
    

    或者

    Promise.reject('出错了')
    .catch(e => {
      console.log(e)
    })
    // 输出 出错了
    

    13,实战用法

    这里列举一些简单的例子,还有很多用处。

    13.1,小程序request

    const Request = (options) =>{
      let url = baseURL + options.url;
      return new Promise((resolve, reject) => {
        wx.request({
          url,
          data: options.data || {},
          method: options.method || 'post',
          responseType: options.responseType || '',
          timeout: 15000,
          success (res) {
            if(res.statusCode === 200){
              resolve(res.data);
            }else{
              FN.Toast(res.errMsg);
            };
          },
          fail (res) {
            FN.Toast("网络开小差了");
            reject(res);
          }
        })
      })
    }
    

    13.2,图片加载

    const preloadImage = function (path) {
      return new Promise(function (resolve, reject) {
        const image = new Image();
        image.onload  = resolve;
        image.onerror = reject;
        image.src = path;
      });
    }
    

    13.3,封装Toast

    import { Message, MessageBox} from 'element-ui'
    
    /**
    * 提示框
    * @param {String} text
    */
    alert(text) {
      return new Promise((resolve, reject) => {
        MessageBox.alert(text, '温馨提示', {
          confirmButtonText: '确定',
          callback: action => {
            if (action === 'confirm'){
                resolve(action)
            } else {
                reject(action)
            }
          }
        })
      })
    }
    

    14,手写Promise

    class myPromise {
      constructor(executor) {
        this.initState()
        this.initBind()
        try {
          executor(this.resolve, this.reject)
        } catch (error) {
          this.reject(error)
        }
      }
      initState() {
        this.result = null
        this.state = 'pending'
        this.resolveCallback = []
        this.rejectCallback = []
      }
      initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
      }
      resolve(value) {
        if (this.state !== 'pending') return
        this.result = value
        this.state = 'success'
        while (this.resolveCallback.length) {
          this.resolveCallback.shift()(this.result)
        }
      }
      reject(value) {
        if (this.state !== 'pending') return
        this.result = value
        this.state = 'error'
        while (this.rejectCallback.length) {
          this.rejectCallback.shift()(this.result)
        }
      }
      then(onResolve, onReject) {
        onResolve = typeof onResolve === 'function' ? onResolve : res => res
        onReject = typeof onReject === 'function' ? onReject : res => res
        switch (this.state) {
          case 'pending':
            this.resolveCallback.push(onResolve.bind(this))
            this.rejectCallback.push(onReject.bind(this))
            break
          case 'success':
            onResolve(this.result)
            break
          case 'error':
            onReject(this.result)
            break
        }
      }
      catch(onReject) {
        return this.then(null, onReject)
      }
    }
    
    const fn = new myPromise((resolve, reject) => {
      setTimeout((res) => {
        resolve('成功')
      }, 2000)
    })
    .then(res => {
      console.log(res)
    })
    

    如果看了觉得有帮助的,我是@鹏多多11997110103,欢迎 点赞 关注 评论;
    END

    往期文章

    个人主页

    相关文章

      网友评论

          本文标题:Promise教程及用法

          本文链接:https://www.haomeiwen.com/subject/dijmbltx.html