应用场景
当后续的Promise需要用到之前的Promise的处理结果时,需要Promise的串联。
如何使用Promise的串联?
举例说明:获取李华所在班级的老师的信息。
思路:
- 根据李华的id获取所在班级的id;
- 根据班级id获取老师的id;
- 根据老师的id获取老师的信息。
ES5的写法造成回调地狱。
Promise对象中,无论是then方法还是catch方法,它们都具有返回值,返回的是一个全新的Promise对象,它的状态满足下面的规则:
-
如果当前的Promise是未决状态,得到的新的Promise是挂起状态;
第一个Promise服务器还没有数据返回,还没有结果,所以得到的新的Promise也是挂起状态
// 获取学生数据 const pro = ajax({ // 挂起状态 url: "./data/students.json" }) const pro2 = pro.then(resp =>{ // 挂起状态 }) console.log(pro2) // Promise pedding -
如果当前的Promise是已决状态,会运行相应的后续处理函数;并将后续处理函数的结果(返回值)作为resolved状态数据,应用到新的Promise中;如果后续处理函数发生错误,则把返回值作为rejected状态数据,应用到新的Promise中。
// 获取学生数据 const pro = ajax({ url: "./data/students.json" }) const pro2 = pro.then(resp => { for (let i = 0; i < resp.length; i++) { if (resp[i].name === "李华") { return resp[i].classId; //班级id } } }) console.log(pro2); // 新的Promise分析:
- 当pro.then()还没有运行里面的函数,此时的pro是未决状态
- 一旦pro.then()运行了里面的函数,pro2变成了已决状态。 该已决状态是resolved还是rejected,取决于pro.then()里有没有错误;没有错误,就是resolved,有错误,就是rejected。 举例说明:classId找到之后再进行后续处理
- 状态数据就是pro.then()的返回值
后续的Promise一定会等到前面的Promise有了后续处理结果后,才会变成已决状态
举例说明:
const pro1 = new Promise((resolve, reject) => { resolve(1); }) const pro2 = pro1.then(result => result * 2); console.log(pro2); // pro2类型:Promise对象 // pro2状态:pending分析:
- 虽然pro1是已决状态
- 但是,Promise的then里面的函数是异步调用的,要到事件队列里面去
- 所以,运行到
console.log(pro2);的时候,pro1的then里面的函数还没有调用。console.log(pro2);是同步代码。 - 既然没有调用,那pro1的后续处理还没有处理。虽然pro1的状态变成了resolved,但是还没有进行后续处理。
- 还没有进行后续处理,就还没有一个已处理的结果
- 没有已处理的结果,pro2就还在等
- pro2的状态一定是前面一个Promise已经处理得到一个结果,才会变成已决
例子一:打印输出结果
const pro1 = new Promise((resolve, reject) => { resolve(1); }) const pro2 = pro1.then(result => result * 2); pro2.then(result => console.log(result), error => console.log(error)) // 输出? 2分析:
- pro1一定变成已决 resolved状态;
- 函数
result => result * 2一定会执行,执行完返回结果; - 有了结果后,pro2才变成已决;
- pro2变成已决之后,返回的结果
result * 2是状态数据; - 最后执行pro2的
console.log(result).
例子二:打印输出结果
const pro1 = new Promise((resolve, reject) => { throw 1; }) const pro2 = pro1.then(result => result * 2, error => error * 3); pro2.then(result => console.log(result * 2), error => console.log(error * 3)) // 输出? 3分析:
new Promise((resolve, reject) => { throw 1; })会把状态推向reject;- 所以,运行函数
error => error * 3,得到一个结果3; - pro2变成已决,接着运行then;
- 因为上一个运行没有错误,所以运行
result => console.log(result * 2); - 如果上一个处理有错误,则运行
error => console.log(error * 3)
判断题:
then返回的Promise一开始一定是挂起状态?正确
分析: then里面的后续处理是异步的,等到同步代码执行完之后才会执行。既然一开始没出结果,那就一定是挂起状态。
小细节: 如果前面的Promise(pro1)的后续处理,返回的是一个Promise(pro2),则返回的新的Promise(pro3)的状态和后续处理返回的Promise(pro2)状态保持一致。(也就是下例中的pro3和pro2的状态保持一致。 )
const pro1 = new Promise((resolve,reject)=>{
resolve(1);
})
const pro2 = new Promise((reject,resolve)=>{
resolve(2);
})
const pro3 = pro1.then(result=>{
return pro2;
})
分析:
- 按照之前的说法,pro2应该是pro3的状态数据
- 但是,由于返回的是Promise,所以,pro3的后续处理的状态数据和pro2的状态数据保持一致,即 2 。
修改代码:
const pro1 = new Promise((resolve, reject) => {
resolve(1);
})
const pro2 = new Promise((reject, resolve) => {
setTimeout(function () {
resolve(2);
}, 3000)
})
const pro3 = pro1.then(result => {
return pro2;
})
pro3.then(result => {
console.log(result === 2)
})
// pro2和pro3同时等三秒输出 2
简化代码:
const pro1 = new Promise((resolve, reject) => {
resolve(1);
})
const pro2 = new Promise((reject, resolve) => {
setTimeout(function () {
resolve(2);
}, 3000)
})
pro1.then(result => {
console.log('结果出来了,得到的是一个Promise')
return pro2;
}).then(result => {
console.log(result)
}).then(result => {
console.log(result)
})
// 输出 结果出来了,得到的是一个Promise
// 隔三秒输出 2
// 马上 输出 undefined
举例:获取李华所在班级的老师的信息。
思路:
根据李华的id获取所在班级的id; 根据班级id获取老师的id; 根据老师的id获取老师的信息。
const pro = ajax({
url: "./data/students.json"
})
pro.then(resp => {
for (let i = 0; i < resp.length; i++) {
if (resp[i].name === "李华") {
return resp[i].classId; //班级id
}
}
}).then(cid => {
return ajax({
url: "./data/classes.json?cid=" + cid
}).then(cls => {
for (let i = 0; i < cls.length; i++) {
if (cls[i].id === cid) {
return cls[i].teacherId;
}
}
})
}).then(tid => {
return ajax({
url: "./data/teachers.json"
}).then(ts => {
for (let i = 0; i < ts.length; i++) {
if (ts[i].id === tid) {
return ts[i];
}
}
})
}).then(teacher => {
console.log(teacher);
})
举例: 垦哥心中有三个女神 有一天,垦哥决定向第一个女神表白,如果女神拒绝,则向第二个女神表白,直到所有的女神都拒绝,或有一个女神同意为止 用代码模拟上面的场景
function biaobai(god) {
return new Promise(resolve => {
console.log(`邓哥向${god}发出了表白短信`);
setTimeout(() => {
if (Math.random() < 0.3) {
//女神同意拉
resolve(true)
} else {
//resolve
resolve(false);
}
}, 500);
})
}
biaobai('女神1').then(resp => {
if (resp) { // 女神1同意
console.log('女神1同意了')
return;
} else {
return biaobai('女神2');
}
}).then(resp => { // 可能有三种情况
if (resp === undefined) { //女神1已经成功
return;
} else if (resp) { // 女神2同意了
console.log('女神2同意了')
return;
} else { // 女神2拒绝了,表白第三个女神
biaobai('女神3');
}
}).then(resp => {
if(resp === undefined){ // 前面已经成功
return;
} else if(resp){ // 女神3同意了
console.log('女神3同意了')
return;
} else {
console.log('都被拒绝了')
}
})
扩展以上代码
- 循环处理
- 设置变量,接收最新的Promise
const gods = ['女神1', '女神2', '女神3'];
let pro;
for (let i = 0; i < gods.length; i++) {
if (i === 0) {
pro = biaobai(gods[i])
}
pro = pro.then(resp => {
if (resp === undefined) {
return;
} else if (resp) {
console.log(`${gods[i]}同意了`)
return;
} else {
console.log(`${gods[i]}拒绝了`)
if (i < gods.length - 1) {
return biaobai(gods[i + 1]);
}
}
})
}
核心:后续处理也会返回Promise