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)

原文地址:https://www.cnblogs.com/cathy1024/p/12267891.html