• 深入理解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('全部图片加载完成之后要处理的事情')
            })
            
        }

                  

  • 相关阅读:
    更改套接字I/O缓冲大小
    读取创建套接字时默认IO缓冲大小
    利用getsockopt读取套接字可选项
    如何查看安装的ubuntu是多少位的系统
    使用虚函数所带来的扩展性
    python学习第17天----接口类/抽象类、多态、封装
    python学习第16天----继承、查找顺序(深度、广度优先)
    python学习第15天----名称空间、组合
    python学习第14天----函数复习、面向对象初始
    python学习第13天----lambda、sorted、map、filter、递归、二分查找
  • 原文地址:https://www.cnblogs.com/wjyz/p/10214971.html
Copyright © 2020-2023  润新知