Promise基本使用与深入
一、学习之前应该了解的知识
函数对象与实例对象
- 函数对象: 将函数作为对象使用时, 简称为函数对象
- 实例对象: new 函数产生的对象, 简称为对象
回调函数的分类
-
同步回调:
理解: 立即执行, 完全执行完了才结束, 不会放入回调队列中
例子: 数组遍历相关的回调函数 / Promise的excutor函数 -
异步回调:
理解: 不会立即执行, 会放入回调队列中将来执行
例子: 定时器回调 / ajax回调 / Promise的成功|失败的回调
二、Promise的理解和使用
1、Promise是什么?
-
抽象表达:
Promise是JS中进行异步编程的新的解决方案 -
具体表达:
从语法上来说: Promise是一个构造函数
从功能上来说: promise对象用来封装一个异步操作并可以获取其结果 -
Promise的状态改变(只有2种, 只能改变一次)
pending
(初始状态)变为resolved
(成功)pending
(初始状态)变为rejected
(失败) -
Promise的基本流程
2、为什么要用Promise?
- 主要解决异步深层嵌套的问题,支持链式调用, 可以解决回调地狱问题
- promise 提供了简洁的API 使得异步操作更加容易
- 指定回调函数的方式更加灵活: 可以在请求发出甚至结束后指定回调函数
3、如何使用Promise?
1. Promise基本使用
我们使用new来构建一个Promise ,Promise的构造函数接收一个参数,是函数,并且传入两个参数: resolve,reject, 分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数
var p = new Promise(function(resolve, reject){
//这里用于实现异步任务 setTimeout
setTimeout(function(){
var flag = false;
if(flag) {
//正常情况
resolve('hello');
}else{
//异常情况
reject('出错了');
}
}, 100);
});
// Promise实例生成以后,可以用then方法指定resolved状态和reject状态的回调函数
// 在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了
p.then(function(data){
console.log(data)
},function(info){
console.log(info)
});
2. 基于Promise发送Ajax请求
//基于Promise发送Ajax请求
function queryData(url) {
//1 创建一个Promise实例
var p = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
//2 处理正常的情况
resolve(xhr.responseText);
}else{
//3 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
return p;
}
//注意:这里需要开启一个后台服务
//在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了
queryData('http://localhost:3000/data')
.then(function(data){
console.log(data)
//4 想要继续链式编程下去 需要 return
return queryData('http://localhost:3000/data1');
})
.then(function(data){
console.log(data);
return queryData('http://localhost:3000/data2');
})
.then(function(data){
console.log(data)
});
3.Promise 基本API
(一)主要API
Promise构造函数: Promise (excutor) {}
Promise.prototype.then方法: (onResolved, onRejected) => {}
Promise.prototype.catch方法: (onRejected) => {}
Promise.resolve方法: (value) => {}
Promise.reject方法: (reason) => {}
Promise.all方法: (promises) => {}
Promise.race方法: (promises) => {}
(二)Promise实例方法
Promise常用API-实例方法
console.dir(Promise);
如图是Promise的打印结果,可以看到Promise对象的实例方法
.then()
- 得到异步任务正确的结果
.catch()
- 获取异常信息
.finally()
- 成功与否都会执行(不是正式标准)
// Promise常用API-实例方法
// console.dir(Promise);
function foo() {
return new Promise(function(resolve, reject){
setTimeout(function(){
// resolve(123);
reject('error');
}, 100);
})
}
// foo()
// .then(function(data){
// console.log(data)
// })
// .catch(function(data){
// console.log(data)
// })
// .finally(function(){
// console.log('finished')
// });
// --------------------------
// 两种写法是等效的,.then()方法可以传递两个参数分别对应成功与失败
foo()
.then(function(data){
// 得到异步任务正确的结果
console.log(data)
},function(data){
// 获取异常信息
console.log(data)
})
// 成功与否都会执行(不是正式标准)
.finally(function(){
console.log('finished')
});
(三)Promise静态(对象)方法
.all()
-
Promise.all
方法接受一个数组作参数,数组中的对象(p1、p2、p3)均为promise实例(如果不是一个promise,该项会被用Promise.resolve
转换为一个promise)。它的状态由这三个promise实例决定
.race()
-
Promise.race
方法同样接受一个数组作参数。当p1, p2, p3中有一个实例的状态发生改变(变为fulfilled
或rejected
),p的状态就跟着改变。并把第一个改变状态的promise的返回值,传给p的回调函数
// Promise常用API-对象方法
// console.dir(Promise)
function queryData(url) {
return new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
// 处理正常的情况
resolve(xhr.responseText);
}else{
// 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
var p1 = queryData('http://localhost:3000/a1');
var p2 = queryData('http://localhost:3000/a2');
var p3 = queryData('http://localhost:3000/a3');
Promise.all([p1,p2,p3]).then(function(result){
// all 中的参数 [p1,p2,p3] 和 返回的结果一 一对应["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
console.log(result) //["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
})
Promise.race([p1,p2,p3]).then(function(result){
// 由于p1执行较快,Promise的then()将获得结果'P1'。p2,p3仍在继续执行,但执行结果将被丢弃。
console.log(result) // "HELLO TOM"
})
三、Promise几个重要问题
1、如何改变promise的状态?
(1)resolve(value): 如果当前是pendding就会变为resolved
(2)reject(reason): 如果当前是pendding就会变为rejected
(3)抛出异常: 如果当前是pendding就会变为rejected
2、一个promise指定多个成功/失败回调函数, 都会调用吗?
- 当promise改变为对应状态时都会调用
const p = new Promise((resolve, reject) => {
// resolve(1) // promise变为resolved成功状态
// reject(2) // promise变为rejected失败状态
// throw new Error('出错了') // 抛出异常, promse变为rejected失败状态, reason为 抛出的error
throw 3 // 抛出异常, promse变为rejected失败状态, reason为 抛出的3
})
p.then(
value => {},
reason => {console.log('reason', reason)}
)
p.then(
value => {},
reason => {console.log('reason2', reason)}
)
3、改变promise状态和指定回调函数谁先谁后?
(1)都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
(2)如何先改状态再指定回调?
- ①在执行器中直接调用resolve()/reject()
- ②延迟更长时间才调用then()
(3)什么时候才能得到数据?
- ①如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
- ②如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
// 常规: 先指定回调函数, 后改变的状态
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // 后改变的状态(同时指定数据), 异步执行回调函数
}, 1000);
}).then(// 先指定回调函数, 保存当前指定的回调函数
value => {},
reason => {console.log('reason', reason)}
)
// 如何先改状态, 后指定回调函数
new Promise((resolve, reject) => {
resolve(1) // 先改变的状态(同时指定数据)
}).then(// 后指定回调函数, 异步执行回调函数
value => {console.log('value2', value)},
reason => {console.log('reason2', reason)}
)
console.log('-------')
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // 后改变的状态(同时指定数据), 异步执行回调函数
}, 1000);
})
setTimeout(() => {
p.then(
value => {console.log('value3', value)},
reason => {console.log('reason3', reason)}
)
}, 1100);
4、promise.then()返回的新promise的结果状态由什么决定?
(1)简单表达: 由then()指定的回调函数执行的结果决定
(2)详细表达:
- ①如果抛出异常, 新promise变为rejected, reason为抛出的异常
- ②如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值
- ③如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
5、promise如何串连多个操作任务?
(1)promise的then()返回一个新的promise, 可以看成then()的链式调用
(2)通过then的链式调用串连多个同步/异步任务
6.promise异常传/穿透?
(1)当使用promise的then链式调用时, 可以在最后指定失败的回调,
(2)前面任何操作出了异常, 都会传到最后失败的回调中处理
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行任务1(异步)")
resolve(1)
}, 1000);
}).then(
value => {
console.log('任务1的结果: ', value)
console.log('执行任务2(同步)')
return 2
}
).then(
value => {
console.log('任务2的结果:', value)
return new Promise((resolve, reject) => {
// 启动任务3(异步)
setTimeout(() => {
console.log('执行任务3(异步))')
resolve(3)
}, 1000);
})
}
).then(
value => {
console.log('任务3的结果: ', value)
}
)
7.中断promise链?
(1)当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数
(2)办法: 在回调函数中返回一个pendding状态的promise对象
上一篇: shell循环:while until