zoukankan      html  css  js  c++  java
  • 深入理解promise

      如今promise大行其道,关于异步方面的几乎都有它的影子,新的fetch api返回的是promise对象,generator中的yield后面一般也会跟promise对象,async的await后面一般也是promise对象。

      既然promise这么重要,这里也整理了一些关于它的知识,加深下自己对promise的理解。

    1,基础

      promise是一个异步操作的最终结果。它有三种状态,初始状态为Pending,状态只能转变为resolved或者rejected,并且不可逆。

      基本用法为

    let promise = new Promise((resolve, reject) => {
    //异步代码....

    if (/*操作成功 */) { resolve(result) } else { reject(error) } })

      必须有then方法,接受两个可选的函数参数:onResolve和onRejected。then方法必须返回一个promise对象以便接下来的链式调用。为了保证其中回调的执行顺序,回调必须使用异步。

      

    2,API

      实例方法:

        .then(onResolve, onReject)   :     promise状态改变时的回调,返回一个新的promise实例

        .catch()   :     本质上就是then方法中的onReject,返回值是一个新的promise

      静态方法:

        Promise.resolve()  :  1,将对象转为promise对象。

                  2,如果参数是promise实例,直接返回

                  3,如果参数为thenable对象,则转换为promise对象,并执行then方法

                  4,如果参数为原始类型,返回一个promise对象,状态为resolved,参数为那个原始类型

                  5,如果没有参数则返回一个状态为resolved的promise的对象

        Promise.reject()  : 和上面的差不多,只是状态改变为rejected

        Promise.all()   :      1,接收一个promise实例的数组

                  2,如果全部成功,则状态转为resolved,返回值为一个数组

                  3,只要有一个失败,状态就转为rejected,直接返回错误

                  4,返回值也是为新的promise对象

        Promise.race()   :    和all差不多,不同的是只要有一个实例的状态改变了就结束了。

    3,promise的误区

      1,把promise当做callback来使用

        

    getData1().then(res1 => {
            getData2(res).then(res2 => {
                getData3(res).then(......)
            })
    })

        promise的目的本来就是用来解决异步回调地狱的,这种写法虽然可行,但完全违背了promise的设计初衷。

        可以改成以下的写法:

    getData1()
            .then(res1 => {
                return getData2(res1)
            })
            .then(res2 => {
                return getData3(res2)
            })
            .then(......)

      2,没有处理错误    

        如果在使用promise时没有处理错误,promise抛出的错误将不会传递到外层代码,即不会有任何反应,不会终止当前脚本的继续执行。

        promise捕获错误有两种方式: 一种是在then中定义rejected状态的回调(then的第二个参数),第二种是用catch方法

        由于promise对象的错误具有冒泡的性质,会一直向后传递,也就是说错误总是会被下一个catch捕获。所以一般来说,不要在then方法中定义rejected状态的回调,而总是使用catch来捕获错误。使用catch可以捕获到前面的then方法中执行的错误。

    4,promise.resolve()

       promise.resolve还有一个作用,就是将现有的对象转为promise对象

       thenable对象指的是具有then方法的对象。比如:

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

        promise.resolve方法会将这个对象转为Promise对象,然后立即执行里面的then方法。

    let thenable = {
            then: function (resolve, reject) {
                resolve(123)
            }
        }
        let p = Promise.resolve(thenable)
        p.then(result => {
            console.log(result)         //123
        })

         then方法执行后,对象p的状态就变为resolved,从而立即执行紧跟着的then方法的回调函数。


    5,promise.reject()

    let p = Promise.reject('出错了')
        等同于
    let p = new Promise((resolve, reject) => reject('出错了'))

      Promise.reject()的参数会原封不动的作为后续方法的参数,这一点与Promise.resolve方法不一致。

    const thenable = {
            then (resolve, reject) {
                reject('错误!')
            }
        }
        Promise.reject(thenable).catch(e => {
            console.log(e === thenable)           // true
        })

      上面的代码中,Promise.reject()的参数为一个thenable对象,执行之后,后面catch方法的参数不是reject抛出的错误这个字符串,而是thenable对象。

     

    6,附加方法

      1,done()

        由于Promise的错误不会冒泡到全局,所以Promise链式调用时,最后一个方法如果抛出错误,不管是then还是catch,都有可能无法捕捉到,这里的done方法,总是处于回调链的末端,保证抛出的任何错误都能被捕捉到。 

    Promise.prototype.done = (onResolved, onRejected) => {
        this.then(onResolved, onRejected).catch( reason => {
            setTimeout(() => {throw reason}, 0)
        })
    }

        done方法可以像then方法那样使用,提供resolved和rejected状态的回调函数。

      2,finally()

        不管Promise对象最后的状态如何,finally参数的回调函数都会执行。

    Promise.prototype.finally = callback => {
        let P = this.constructor
        return this.then(
            value => P.resolve(callback()).then(() => value),
            reason => P.reject(callback()).then(() => {throw reason})
        )
    }

    7,实战应用

      图片懒加载

        let images = []
        
        function loadImg(url) {
            return new Promise((resolve, reject) => {
                let img = new Image()
                img.onload = () => resolve(img)
                img.onerror = reject
                img.src = url
            })
        }
        function preLoadImg(imgs) {
            let arr = []
            imgs.forEach(url => {
                let image = loadImg(url).then(img => images.push(img)).catch(error => console.log(error))
                arr.push(image)
            })
            
            Promise.all(arr).then(() => {
                console.log('全部图片加载完成之后要处理的事情')
            })
            
        }

                  

  • 相关阅读:
    解决ListView异步加载数据之后不能点击的问题
    android点击实现图片放大缩小 java技术博客
    关于 数据文件自增长 的一点理解
    RAC 实例不能启动 ORA1589 signalled during ALTER DATABASE OPEN
    Linux 超级用户的权利
    RAC 实例 迁移到 单实例 使用导出导入
    Shell 基本语法
    Linux 开机引导与关机过程
    RAC 实例不能启动 ORA1589 signalled during ALTER DATABASE OPEN
    Oracle RAC + Data Guard 环境搭建
  • 原文地址:https://www.cnblogs.com/wjyz/p/10214971.html
Copyright © 2011-2022 走看看