文章目录
- 一、含义
- 二、用法
-
- 1、基本使用
- 2、resolve函数和reject函数带有参数
-
- [2.1 resolve](#2.1 resolve)
-
- [2.1.1 参数为正常值的情况](#2.1.1 参数为正常值的情况)
- [2.1.2 参数为Promise 实例的情况](#2.1.2 参数为Promise 实例的情况)
- 2.2reject
- 3、Promise.prototype.then()
- 4、Promise.prototype.catch()
- 5、Promise.prototype.finally()
- 6、Promise.all()
- 7、Promise.resolve()
- 8、Promise.reject()
- 三、练习题
一、含义
1、是什么
Promise 是异步编程的一种解决方案
2、特点
Promise对象有以下两个特点:
1、对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果
二、用法
1、基本使用
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例
代码示例:
js
const promise = new Promise(function (resolve, reject) {
// ... some code
console.log('这里的代码是同步代码,会立即执行~');
let flag = true;
// let flag = false;
if (flag) {
resolve(true);
} else {
reject(false);
}
});
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数:
js
promise.then(
function (value) {
// success
// Promise对象的状态变为resolved时调用
},
function (error) {
// failure
// Promise对象的状态变为rejected时调用
}
);
示例:timeout
js
function timeout(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('done');
}, time);
});
}
timeout(2000).then((value) => {
console.log(value);
});
示例:异步加载图片
js
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
2、resolve函数和reject函数带有参数
如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。
2.1 resolve
2.1.1 参数为正常值的情况
js
const p = new Promise((resolve, reject) => {
resolve('hello');
});
p.then((value) => {
console.log(value); // hello
});
2.1.2 参数为Promise 实例的情况
当一个 Promise 实例在 resolve 方法中返回另一个 Promise 实例时,当前 Promise 的最终状态将由内部 Promise 实例的状态决定
如下示例:
p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态
如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行
js
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('这是p1的resolve');
// reject('这是p1的reject');
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
// p2 resolve传递 p1 实例
resolve(p1);
}, 1000);
});
p2.then((value) => {
console.log('resolve--', value); // resolve--这是p1的resolve
}).catch((error) => {
console.log('reject--', error);
});
2.2reject
reject函数的参数通常是Error对象的实例,表示抛出的错误
js
const p = new Promise((resolve, reject) => {
reject(new Error('出错了'));
});
p.catch((error) => {
console.log('报错', error); // 报错Error{}
});
注意,调用resolve或reject并不会终结 Promise 的参数函数的执行。
js
new Promise((resolve, reject) => {
resolve(1);
console.log(2); // 这里仍然会执行
}).then(r => {
console.log(r);
});
// 2
// 1
3、Promise.prototype.then()
(1)then方法参数:
- 第一个参数是 resolved 状态的回调函数(可选的)
- 第二个参数是 rejected 状态的回调函数(可选的)
(2)then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例),因此可以采用链式写法
js
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
(3)当前then里面的回调函数返回的还是一个Promise对象(即有异步操作)时,这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用
js
function getCount(count) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(count + 1);
}, 2000);
});
}
getCount(1)
.then((val) => {
console.log(val); // 2
// 返回的还是一个Promise对象
return getCount(val);
})
.then((val) => {
console.log(val); // 3
});
4、Promise.prototype.catch()
(1)Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
js
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
(2)如果 Promise 状态已经变成resolved,再抛出错误是无效的。
js
const 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
(3)Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
js
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
5、Promise.prototype.finally()
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
js
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
6、Promise.all()
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
js
const p = Promise.all([p1, p2, p3]); // p1、p2、p3都是 Promise 实例
p的状态由p1、p2、p3决定,分成两种情况:
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
示例:
js
const p1 = new Promise(function (resolve, reject) {
resolve(1);
});
const p2 = new Promise(function (resolve, reject) {
resolve(2);
});
const p3 = new Promise(function (resolve, reject) {
resolve(3);
});
const p = Promise.all([p1, p2, p3]);
p.then((list) => {
console.log(list); // [1, 2, 3]
});
7、Promise.resolve()
将现有对象转为 Promise 对象
js
Promise.resolve('foo')
等价于:
js
new Promise(resolve => resolve('foo'))
8、Promise.reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
js
const p = Promise.reject('出错了');
等同于
js
const p = new Promise((resolve, reject) => reject('出错了'))
三、练习题
1、第1题
js
new Promise((resolve, reject) => {
console.log(1);
resolve();
})
.then(() => {
console.log(2);
return new Promise((resove) => {
console.log(4)
resove();
})
.then(() => {
console.log(5);
})
.then(() => {
console.log(6);
});
})
.then(() => {
console.log(3);
});
解析:
(1)外部第一个 new Promise 执行,执行完 resolve ,然后执行外部第一个 then
(2)外部第一个 then 方法里面 return 一个 Promise,这个 return 代表 外部的第二个 then 的执行需要等待 return 之后的结果
(3)执行完内部两个 then 之后,再执行 外部的第二个 then
2、第2题
注意:和第一题相差一个 return
js
// 外部 promise
new Promise((resolve, reject) => {
console.log(1);
resolve();
})
.then(() => {
// 外部第一个 then
console.log(2);
// 内部 promise
new Promise((resove) => {
console.log(4);
resove();
})
.then(() => {
// 内部第一个then
console.log(5);
})
.then(() => {
// 内部第二个then
console.log(6);
});
})
.then(() => {
// 外部第二个then
console.log(3);
});
解析:
重点: 下一个 then 的注册,需要等待上一个 then 的同步代码执行完成
执行顺序:
- 外部的 promise 立即执行,打印1;外部 promise 进行 resolve 后,执行外部第一个 then 的注册,外部第二个 then 进入等待状态
- 执行外部第一个 then, 打印2 ;立即执行内部的 promise 打印4;进行 resolve后,内部的第一个 then 注册,内部的第二个 then 进入等待状态;此时外部的第一个 then 的同步操作已经完成,然后开始注册外部第二个 then
- 执行内部第一个 then (因为内部第一个then是比外部第一个then先注册的),打印5,然后注册内部第二个 then;
- 执行外部第二个 then,打印3;
- 执行内部第二个 then,打印6
所以最终的顺序:
js
1
2
4
5
3
6
3、第3题
注意:内部第二个then不是链式调用
js
// 外部 promise
new Promise((resolve, reject) => {
console.log(1);
resolve();
})
.then(() => {
// 外部第一个 then
console.log(2);
// 内部 promise
let p = new Promise((resolve, reject) => {
console.log(4);
resolve();
});
// 内部第一个then
p.then(() => {
console.log(5);
});
// 内部第二个then,注意这里不是链式调用,这里是跟内部第一个then是同步注册的
p.then(() => {
console.log(6);
});
})
.then(() => {
// 外部第二个 then
console.log(3);
});
解析:
执行内部的 Promise 的 resolve 执行之后,两个同步 p.then 是两个执行代码语句,都是同步执行,自然是会同步注册完。
输出结果:
js
1
2
4
5
6
3
4、第4题
js
let p = new Promise((resolve, reject) => {
console.log(1);
resolve();
});
p.then(() => {
console.log(2);
new Promise((resolve, reject) => {
console.log(3);
resolve();
})
.then(() => {
console.log(5);
})
.then(() => {
console.log(6);
});
});
p.then(() => {
console.log(4);
});
解析:
外部的注册采用了非链式调用的写法,因此外部的 p.then 是并列同步注册的
内部的第一个 then 注册之后,就开始执行外部的第二个 then 了。然后再依次执行内部的第一个 then ,内部的第二个 then
5、第5题
js
new Promise((resolve, reject) => {
console.log(1);
resolve();
})
.then(() => {
console.log(2);
new Promise((resolve, reject) => {
console.log(4);
resolve();
})
.then(() => {
console.log(5);
})
.then(() => {
console.log(6);
});
return new Promise((resolve, reject) => {
console.log(7);
resolve();
})
.then(() => {
console.log(8);
})
.then(() => {
console.log(9);
});
})
.then(() => {
console.log(3);
});
解析:
核心:外部的第二个 then ,依赖于内部的 return 的执行结果,所以会等待 return 执行完成
最终输出:
js
1
2
4
7
5
8
6
9
3