Promise对象学习笔记

参考:阮一峰《ES6标准入门》(第3版)第14章 Promise对象

Promise的含义

Promise是异步编程的一种解决方案

一大波文字省略……

基本用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例

一个简单的Promise实例:

 1 // 创建一个Promise实例
 2 var promise = new Promise(function(resolve, reject) {
 3     // ... some code
 4     if (/*异步操作成功*/) {
 5         resolve('some value')
 6     } else {
 7         reject('some error')
 8     }
 9 })
10 
11 // 调用promise
12 promise.then(function(value) {
13     // success
14 }, function (error) {
15     // failure
16 })

另一个Promise对象的简单例子

 1 function timeout (ms) {
 2     return new Promise((resolve, reject) => {
 3         setTimeout(resolve, ms, 'done')
 4     })
 5 }
 6 
 7 timeout(3000).then((value) => {
 8     console.log(value)
 9 })
10 
11 // 输出结果:
12 // done

上面代码中,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为Resolved,就会触发then方法绑定的回调函数。

下面这个例子可以看到promise的执行顺序

 1 let promise = new Promise(function(resolve, reject) {
 2     console.log('Promise');
 3     resolve();
 4 })
 5 
 6 promise.then(function() {
 7     console.log('Resolved.');
 8 })
 9 
10 console.log('Hi!');
11 
12 // 执行顺序
13 // Promise
14 // Hi!
15 // Resolved.

上面的代码中,Promise新建后会立即执行,所以首先输出的是Promise, 然后,then方法指定的回调函数将在当前脚本所有同步任务执行完成后才会执行,所以Resolved最后输出。

一个异步加载图片的例子

为了直观看到效果,新建一个html文件index.html

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6     <meta http-equiv="X-UA-Compatible" content="ie=edge">
 7     <title>Document</title>
 8 </head>
 9 <body>
10     <script>
11         function loadImageAsync (url) {
12             return new Promise(function(resolve, reject) {
13                 var image = new Image()
14                 image.src = url;
15 
16                 image.onload = function () {
17                     resolve(image);
18                 }
19 
20                 image.onerror = function () {
21                     reject(new Error('Could not load image at' + url))
22                 }
23             })
24         }
25 
26         loadImageAsync('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1580879109326&di=c7df42cf94ebc4b5497f5982f8473b5a&imgtype=0&src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F35e6a6c1866c50ade317846362997b03f7ffe5efc732-bTFyZ7_fw658').then(function(image){
27             var body = document.body;
28             body.append(image)
29         }, function(error){
30 
31         })
32     </script>
33 </body>
34 </html>

测试图片url网上随便找的,浏览器预览index.html效果如下:

一个用Promise对象实现AJAX操作的例子

 1 var getJSON = function (url) {
 2     var promise = new Promise(function (resolve, reject) {
 3         var client = new XMLHttpRequest();
 4         client.open("GET", url);
 5         client.onreadystatechange = handler;
 6         client.responseType = "json";
 7         client.setRequestHeader("Accept", "application/json");
 8         client.send();
 9 
10         function handler() {
11             if (this.readyState !== 4) {
12                 return;
13             }
14             if (this.status === 200) {
15                 resolve(this.response);
16             } else {
17                 reject(new Error(this.statusText))
18             }
19         }
20     });
21 
22     return promise;
23 }
24 
25 getJSON("/posts.json").then(function(json){
26     console.log('Contents: ' + json);
27 }, function (error) {
28     console.error('出错了', error);
29 })

一个更复杂的promise,resolve函数的参数也是一个promise实例

 1 var p1 = new Promise(function(resolve, reject) {
 2     setTimeout(() => {
 3             console.log('p1 done')
 4             
 5             // reject测试例子
 6             // reject(new Error('fail')) 
 7 
 8             // resolve测试例子
 9             resolve('success')
10         }, 3000)
11 })
12 
13 var p2 = new Promise(function(resolve, reject) {
14     setTimeout(() => {
15         console.log('p2 done');
16         resolve(p1)
17     }
18     , 1000);
19 })
20 
21 p2.then(result => console.log(result)).catch(error => console.log(error))

1s后执行p2里面的console.log('p2 done')

3s后执行p1里面的console.log('p1 done')

p1里面各写一个测试例子,当reject一个结果时,p2 catch里面将捕获这个结果,当p1里面resolve一个结果时,p2的then里面将返回这个结果

再来一个非常重要的小例子,说明调用resolve或reject并不会终结Promise的参数函数的执行

new Promise((resolve, reject) => {
    resolve(1)
    console.log(2)
}).then(r => {
    console.log(r);
})

// 执行结果:
// 2
// 1

resolved的Promise是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。

resolve前加上return,可以阻止后面的代码执行

new Promise((resolve, reject) => {
    return resolve(1);
    // 后面的语句不会执行
    console.log(2);
})

 Promise.prototype.then()

Promise实例具有then方法,即then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。

then方法返回的是一个新的Promise实例,因此可以采用链式写法

getJSON("/posts.json").then(function(json){
    console.log(1)
    return json.post;
}).then(function(post){
    console.log(2)
})

再看一个例子:

getJSON("/post/1.json").then(function(post){
    return getJSON(post.commentURL);
}).then(function funcA(comments){
    console.log("Resolved:", comments)
}, function funcB(err) {
    console.log("Rejected:", err)
})

上面代码中,第一个then方法指定的回调函数返回的是另一个Promise对象。这时,第二个then方法指定的回调函数就会等待这个新的Promise对象状态发生变化。如果变为Resolved,就调用funcA;如果变为Rejected,就调用funcB。

 Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

getJSON('/posts.json').then(function(posts){
    // ...
}).catch(function(error){
    // 处理getJSON和前一个回调函数运行时发生的错误
    console.log('发生错误!', error);
})

再看一个例子:

p.then((val) => console.log('fulfilled:', val))
    .catch((err) => console.log('rejected:', err))

// 等同于

p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log('rejected:', err))

再看一个例子:

var promise = new Promise(function(resolve, reject){
    throw new Error('test')
})

promise.catch(function(error){
    console.log(error)
})
// Error: test

上面代码中,Promise抛出一个错误就被catch方法指定的回调函数所捕获。

两种等价写法:

// 写法一
var promise = new Promise(function(resolve, reject){
    try {
        throw new Error('test')
    } catch(e){
        reject(e)
    }
})

promise.catch(function(error){
    console.log(error)
})



// 写法二
var promise = new Promise(function(resolve, reject){
    reject(new Error('test'))
})
promise.catch(function(error){
    console.log(error)
})

比较上面两种写法,可以发现reject方法的作用等同于抛出错误

注意:

如果Promise状态已经变成Resolved,再抛出错误是无效的

请看下面的例子

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在resolve语句后面再抛出错误,并不会被捕获,等于没有抛出。因为Promise的状态一旦改变,就会永久保持该状态,不会再改变了。

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

请看下面的例子

getJSON('/post/1.json').then(function(post){
    return getJSON(post.commentURL);
}).then(function(comments) {
    // some code
}).catch(function(error){
    // 处理前面3个Promise产生的错误
})

一般来说,不要在then方法中定义Rejected状态的回调函数(即then的第二个参数),而应总是使用catch方法。

如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。

2020年立个flag——每天写一篇技术博客(或笔记)。

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