• Promise对象学习笔记(2)


    上一篇:Promise对象学习笔记

    Promise.prototype.catch()

    看一个例子:

    var promise = new Promise(function(resolve, reject) {
        resolve('ok')
        setTimeout(function(){
            throw new Error('test')
        })
    })
    
    promise.then(function(value){
        console.log(value);
    })

    这段代码执行结果如下:

     上面代码中,Promise指定在下一轮“事件循环”再抛出错误。到了那个时候,Promise的运行已经结束,所以这个错误是在Promise函数体外抛出的,会冒泡到最外层,成了未捕获的错误。

    这个例子跟上一篇中一个例子很像,再贴一下代码:

    var promise = new Promise(function(resolve, reject) {
        resolve('ok')
        throw new Error('test')
    })
    
    promise.then(function(value){
        console.log(value);
    }).catch(function(error){
        console.log(error);
    })

    执行结果:ok

     并不会抛出错误。因为Promise状态已经变成Resolved,再抛出错误是无效的。

    再看一个例子

    var 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')
    })

    运行结果:

    catch方法返回的还是一个Promise对象,因此后面还可以接着调用then方法

    上面的代码运行完catch方法指定的回调函数后会接着运行后面那个then方法指定的回调函数。如果没有报错则跳过catch方法。

    Promise.resolve().catch(function(error){
        console.log('oh no', error)
    }).then(function(){
        console.log('carry on')
    })

    执行结果:

    carry on

    上面的代码因为没有报错而跳过catch方法,直接执行了后面的then方法。此时要是then方法里面报错,就与前面的catch无关了

    catch方法中还能再抛出错误

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

    执行结果如下:

    上面的代码中,catch方法抛出一个错误,因为后面没有别的catch方法,导致这个错误不会被捕获,也不会传递到外层。

    改写一下代码:

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

    执行结果如下:

     上面代码中,第二个catch方法用来捕获前一个catch方法抛出的错误。

    Promise.all()

    Promise.all方法用于将多个Promise实例包装成一个新的Promise实例

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

    看一个经典实用的例子

    var promises = [2, 3, 5, 7, 11, 13].map(function(id){
        return getJSON('/post' + id + '.json');
    })
    
    Promise.all(promises).then(function(posts){
        // ...
    }).catch(function(reason) {
        // ...
    })

    上面代码中,promises是包含6个Promise实例的数组,只有这6个实例的状态都变成fulfilled,或者其中一个变为rejected,才会调用Promise.all方法后面的回调函数。

    下面是另一个例子

    1 const databasePromise = connectDatabase()
    2 
    3 const booksPromise = databasePromise.then(findAllBooks)
    4 
    5 const userPromise = databasePromise.then(getCurrentUser)
    6 
    7 Promise.all([booksPromise, userPromise]).then(([books, user]) => {
    8     pickTopRecommentations(books, user)
    9 })

    上面的代码中,booksPromise和userPromise是两个异步操作,只有他们的结果都返回,才会触发pickTopRecommentations回调函数

    注意:

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

    请看下面的例子

     1 const p1 = new Promise(function(resolve, reject){
     2     resolve('hello')
     3 })
     4 .then(result => result)
     5 .catch(e => e)
     6 
     7 const p2 = new Promise(function(resolve, reject) {
     8     throw new Error('报错了')
     9 })
    10 .then(result => result)
    11 .catch(e => e)
    12 
    13 Promise.all([p1, p2])
    14 .then(result => console.log(result))
    15 .catch(e => console.log(e))

    执行结果:["hello", Error: 报错了]

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

    如果p2没有自己的catch方法,就会调用Promise.all()的catch方法

    修改上面的代码:

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

    执行结果:Error: 报错了

    不会走进then方法,直接走进catch方法。

    Promise.race()

    Promise.race方法同样是将多个Promise实例包装成一个新的Promise实例

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

    请看一个实用例子,如果指定时间内没有获得结果,就将Promise的状态变为Rejected,否则变为Resolved

    const p = Promise.race([
        fetch('/resource-that-may-tabke-a-while'),
        new Promise(function(resole, reject){
            setTimeout(() => reject(new Error('request timeout')), 5000)
        })
    ])
    
    p.then(response => console.log(response))
    p.catch(error => console.log(error))

    Promise.resolve()

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

    var jsPromise = Promise.resolve($.ajax('/whatever.json'));

    Promise.resolve方法的参数分成以下4种情况:

    1.参数是一个Promise实例

    如果参数是Promise实例,那么Promise.resolve将不做任何修改,原封不动地返回这个实例

    2.参数是一个thenable对象

    thenable对象指的是具有then方法的对象,比如下面这个对象

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

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

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

    执行结果:42

    3.参数不是具有then方法的对象或根本不是对象

    如果参数是一个原始值,或者是一个不具有then方法的对象,那么Promise.resolve方法返回一个新的Promise对象,状态为resolved.

    var p = Promise.resolve('Hello')
    p.then(function(s){
        console.log(s)
    })

    执行结果:Hello

    4.不带有任何参数

    Promise.resolve方法允许在调用时不带有参数,而直接返回一个Resolved状态的Promise对象

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

    再看一个例子

    setTimeout(function(){
        console.log('three')
    }, 0)
    
    Promise.resolve().then(function(){
        console.log('two')
    })
    
    console.log('one')

    执行结果:

    one

    two

    three

    上面代码中,setTimeout(fn, 0)是在下一轮“事件循环”开始时执行的,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。

    Promise.reject()

    Promise.reject(reason)方法也会返回一个新的Promise实例,状态为Rejected.

    var p = Promise.reject('出错了');
    // 等同于
    var p = new Promise((resolve, reject) => reject('出错了'))
    
    p.then(null, function(s) {
        console.log(s)
    })

    执行结果:出错了

    注意:Promise.reject()方法的参数会原封不动地作为reject的理由变成后续方法的参数。这一点与Promise.resolve方法不一致。

    const thenable = {
        then: function(resolve, reject) {
            reject('出错了')
        }
    }
    
    Promise.reject(thenable).catch(e => {
        console.log(e === thenable)
    })
    
    // true

    两个有用的附加方法

    ES6的Promise API提供的方法不多,可以自己部署一些有用的方法。

    done()

    无论Promise对象的回调链以then方法还是catch方法结尾,只要最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。为此,我们可以提供一个done方法,它总是处于回调链的尾端,保证抛出任何可能出现的错误。

    实现done方法的代码:

    Promise.prototype.done = function(onFulfilled, onRejected) {
        this.then(onFulfilled, onRejected)
            .catch(function(reason){
                // 抛出一个全局错误
                setTimeout(() => {throw reason}, 0)
            })
    }

    finally()

    finally方法用于指定不管Promise对象最后状态如何都会执行的操作。它与done方法的最大区别在于,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

    下面是一个例子,服务器使用Promise处理请求,然后使用finally方法关掉服务器

    server.listen(0)
        .then(function(){
            // run test
        })
        .finally(server.stop)

    finally方法的实现代码:

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

    应用

     加载图片

    我们可以将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化。

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

    Promise.try()

    实际开发中经常遇到一种情况:不知道或者不想区分函数f是同步函数还是异步操作,但是想用Promise来处理它。

    一般写法如下:

    Promise.resolve().then(f)

    这种写法有个缺点,如果f是同步函数,那么它会在本轮事件循环的末尾执行。

     const f = () => console.log('now')
     Promise.resolve().then(f)
     console.log('next')
    
     // 执行结果:
     // next
     // now

    上面代码中,函数f是同步的,但是用Promise包装以后就变成了异步执行了。

    那么有没有一种方法,让同步函数同步执行,让异步函数异步执行,并且让它们具有统一的API呢?回答是有的,并且还有两种写法。

    第一种写法是使用async函数

    const f = () => console.log('now');
    (async () => f())()
    
    console.log('next')
    
    // 执行结果:
    // now
    // next
    const f = () => console.log('now');
    (async () => f())()
    .then(function(){
        console.log('1')
    })
    
    console.log('next')
    
    // 执行结果:
    // now
    // next
    // 1

    需要注意的是,async () => f() 会吃掉f()抛出的错误。所以,如果想捕获错误,要使用promise.catch方法

    请看以下代码:

     1 const f = () => { 
     2     console.log('now')
     3     throw new Error('出错了') 
     4 };
     5 (async () => f())()
     6 .then(function(){
     7     console.log('1')
     8 }).catch(function(error){
     9     console.log('catch error:', error)
    10 })
    11 
    12 console.log('next')

    执行结果如下:

    (end)

  • 相关阅读:
    Python time
    Python List/Tutle/Set/Str/Dict
    python os
    Python 学习之九九乘法表
    Pycharm配置
    python 打包exe:
    Linux 安装PHP扩展过程
    tkinter在循环中创建按钮以传递命令参数,闭包的坑
    tkinter
    设置greenplum用户和密码访问:
  • 原文地址:https://www.cnblogs.com/cathy1024/p/12267891.html
Copyright © 2020-2023  润新知