1. 异步请求处理方式
/**
*
* 这种回调的方式有很多弊端:
* 1> 如果是我们自己封装的requestData,那么我们在封装的时候必须要设计好callback名称,并且使用好
* 2> 如果我们使用的是别人封装的requestData或者一些第三方库,那么我们必须去看别人的源码或者文档,才知道它这个函数需要怎么去获取到结果
*/
function requestData (url, successCallback, failtureCallback) {
// 模拟网络请求
setTimeout(() => {
// 拿到请求结果
if (url === 'arielBo') {
// 成功
let names = ['aa', 'bb', 'nba'];
successCallback(names)
} else { // 否则请求失败
// 失败
let errMessage = "请求失败,url错误";
failtureCallback(errMessage);
}
}, 3000);
}
// main.js
requestData("arielBo1", res => {
console.log(res, 'res---');
}, err => {
console.log(err)
})
更好的解决办法是Promise
2. 我们来看一下Promise的API是怎么样的
-
Promise是一个类,可以翻译成 承诺、许诺 、期约;
-
当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象;
-
在通过new创建Promise对象时,我们需要传入一个回调函数,我们称之为executor
- 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject;
- 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
- 当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;
3. promise的基本使用
上面Promise使用过程,我们可以将它划分成三个状态:
-
待定(pending): 初始状态,既没有被兑现,也没有被拒绝;
-
当执行executor中的代码时,处于该状态;
-
已兑现(fulfilled): 意味着操作成功完成;
-
执行了resolve时,处于该状态;
-
已拒绝(rejected): 意味着操作失败;
-
执行了reject时,处于该状
function foo() {
// Promise
return new Promise((resolve, reject) => {
resolve("success message")
// reject("failture message")
})
}
// main.js
const fooPromise = foo()
// then方法传入的回调函数两个回调函数:
// > 第一个回调函数, 会在Promise执行resolve函数时, 被回调
// > 第二个回调函数, 会在Promise执行reject函数时, 被回调
fooPromise.then((res) => {
console.log(res)
}, (err) => {
console.log(err)
})
// // catch方法传入的回调函数, 会在Promise执行reject函数时, 被回调
fooPromise.catch(() => {
})
// 传入的这个函数, 被称之为 executor
// > resolve: 回调函数, 在成功时, 回调resolve函数
// >reject: 回调函数, 在失败时, 回调reject函数
// const promise = new Promise((resolve, reject) => {
// // console.log("promise传入的函数被执行了")
// // resolve()
// reject()
// })
// promise.then(() => {
// })
// promise.catch(() => {
// })
// 钩子函数: hook
function foo(fn) {
fn()
}
foo(() => {
})
4. 异步请求的promise
js
// request.js
function requestData(url,) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
// url传入的是coderwhy, 请求成功
if (url === "coderwhy") {
// 成功
let names = ["abc", "cba", "nba"]
resolve(names)
} else { // 否则请求失败
// 失败
let errMessage = "请求失败, url错误"
reject(errMessage)
}
}, 3000);
})
}
// main.js
const promise = requestData("coderwhy")
promise.then((res) => {
console.log("请求成功:", res)
}, (err) => {
console.log("请求失败:", err)
})
5. promise的三种状态
// const promise = new Promise((resolve, reject) => {
// })
// promise.then(res => {
// }, err => {
// })
// 完全等价于下面的代码
// 注意: Promise状态一旦确定下来, 那么就是不可更改的(锁定)
new Promise((resolve, reject) => {
// pending状态: 待定/悬而未决的
console.log("--------")
reject() // 处于rejected状态(已拒绝状态)
resolve() // 处于fulfilled状态(已敲定/兑现状态)
console.log("++++++++++++")
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
6. Promise的resolve参数
/**
* resolve(参数)
* 1> 普通的值或者对象 pending -> fulfilled
* 2> 传入一个Promise
* 那么当前的Promise的状态会由传入的Promise来决定
* 相当于状态进行了移交
* 3> 传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口)
* 那么也会执行该then方法, 并且又该then方法决定后续状态
*/
// 1.传入Promise的特殊情况
// const newPromise = new Promise((resolve, reject) => {
// // resolve("aaaaaa")
// reject("err message")
// })
// new Promise((resolve, reject) => {
// // pending -> fulfilled
// resolve(newPromise)
// }).then(res => {
// console.log("res:", res)
// }, err => {
// console.log("err:", err)
// })
// 2.传入一个对象, 这个兑现有then方法
new Promise((resolve, reject) => {
// pending -> fulfilled
const obj = {
then: function(resolve, reject) {
// resolve("resolve message")
reject("reject message")
}
}
resolve(obj)
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
// eatable/runable
const obj = {
eat: function() {
},
run: function() {
}
}
7. Promise对象方法-then
// Promise有哪些对象方法
// console.log(Object.getOwnPropertyDescriptors(Promise.prototype))
const promise = new Promise((resolve, reject) => {
resolve("hahaha")
})
// 1.同一个Promise可以被多次调用then方法
// 当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用
// promise.then(res => {
// console.log("res1:", res)
// })
// promise.then(res => {
// console.log("res2:", res)
// })
// promise.then(res => {
// console.log("res3:", res)
// })
// 2.then方法传入的 "回调函数: 可以有返回值
// then方法本身也是有返回值的, 它的返回值是Promise
// 1> 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值
// promise.then(res => {
// return "aaaaaa"
// }).then(res => {
// console.log("res:", res)
// return "bbbbbb"
// })
// 2> 如果我们返回的是一个Promise
// promise.then(res => {
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve(111111)
// }, 3000)
// })
// }).then(res => {
// console.log("res:", res)
// })
// 3> 如果返回的是一个对象, 并且该对象实现了thenable
promise.then(res => {
return {
then: function(resolve, reject) {
resolve(222222)
}
}
}).then(res => {
console.log("res:", res)
})
8. Promise 对象方法-catch
// const promise = new Promise((resolve, reject) => {
// resolve()
// // reject("rejected status")
// // throw new Error("rejected status")
// })
// 1.当executor抛出异常时, 也是会调用错误(拒绝)捕获的回调函数的
// promise.then(undefined, err => {
// console.log("err:", err)
// console.log("----------")
// })
// 2.通过catch方法来传入错误(拒绝)捕获的回调函数
// promise/a+规范
// promise.catch(err => {
// console.log("err:", err)
// })
// promise.then(res => {
// // return new Promise((resolve, reject) => {
// // reject("then rejected status")
// // })
// throw new Error("error message")
// }).catch(err => {
// console.log("err:", err)
// })
// 3.拒绝捕获的问题(前面课程)
// promise.then(res => {
// }, err => {
// console.log("err:", err)
// })
// const promise = new Promise((resolve, reject) => {
// reject("111111")
// // resolve()
// })
// promise.then(res => {
// }).then(res => {
// throw new Error("then error message")
// }).catch(err => {
// console.log("err:", err)
// })
// promise.catch(err => {
// })
// 4.catch方法的返回值
const promise = new Promise((resolve, reject) => {
reject("111111")
})
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
return "catch return value"
}).then(res => {
console.log("res result:", res)
}).catch(err => {
console.log("err result:", err)
})
9. Promise对象方法-finally
const promise = new Promise((resolve, reject) => {
// resolve("resolve message")
reject("reject message")
})
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
}).finally(() => {
console.log("finally code execute")
})
10. 类方法Promise.resolve
1.普通的值 <对象转成promise>
const promise = Promise.resolve({name: "why"})
相当于
const promise2 = new Promise((resolve, reject) => {
resolve({name: "why"});
})
传入promise
const promise = Promise.resolve(new Promise((resolve, reject) => {
resolve("11111");
}))
promise.then(res => {
console.log("res:", res);
})
11. promise的方法-reject
// 注意: 无论传入什么值都是一样的
const promise = Promise.reject(new Promise(() => {}))
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
12. Promise类方法 - all
另外一个类方法是Promise.all:
它的作用是将多个Promise包裹在一起形成一个新的Promise;
新的Promise状态由包裹的所有Promise共同决定:
当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值
组成一个数组;
当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数;a
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// 需求: 所有的Promise都变成fulfilled时, 再拿到结果
// 意外: 在拿到所有结果之前, 有一个promise变成了rejected, 那么整个promise是rejected
Promise.all([p2, p1, p3, "aaaa"]).then(res => {
console.log(res)
}).catch(err => {
console.log("err:", err)
})
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject({
message: "你是错误的信息"
})
}, 3000);
})
// 需求: 所有的Promise都变成fulfilled时, 再拿到结果
// 意外: 在拿到所有结果之前, 有一个promise变成了rejected, 那么整个promise是rejected
Promise.all([p2, p1, p3, "aaaa"]).then(res => {
console.log(res)
}).catch(err => {
console.log("err:", err)
})
13 Promise allSettled
所有的Promise对象均出现结果(无论成功或失败)后才会执行allSettled中的then回调(只会进入then回调)
all方法有一个缺陷:当有其中一个Promise变成reject状态时,新Promise就会立即变成对应的reject状态。
那么对于resolved的,以及依然处于pending状态的Promise,我们是获取不到对应的结果的;
在ES11(ES2020)中,添加了新的API Promise.allSettled:
该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,才会有最终的状态;
并且这个Promise的结果一定是fulfilled的;
allSettled的结果是一个数组,数组中存放着每一个Promise的结果,并且是对应一个对象的;
p这个对象中包含status状态,以及对应的value值;
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// allSettled
Promise.allSettled([p1, p2, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
使用场景
使用场景: 在Promise.allSettled出现以前,对于需要发送一组ajax请求都是使用Promise.all去处理,待所有参数实例都resolve则可以继续执行下一步操作,但这样会有个弊端,若是有一个参数实例reject,则直接会进入到catch里面,即使其它实例参数resolve也是如此,Promise.all方法resolve的条件在于参数数组中的所有Promise实例都需要resolve才可以,这样显然在一些业务中不适用的,假如一个模块需要显示三部分内容,每一部分内容都有一个返回Promise实例的接口,如果使用Promise.all需要三个接口都成功返回数据才可以,如果有一个接口挂掉了,则另外两个接口返回的数据不能被获取到,因为此时已经进入到了catch方法,无法在成功回调的函数里面操作数据,渲染界面等。此时Promise.allSettled便派上用场了。无论参数实例是否reject,最终Promise.allSettled内部都会resolve,只不过会添加一个状态status来记录对应的参数实例是否执行成功。我们可以依据这个状态去过滤掉rejected的数据,只操作fulfilled的数据,就会得到我们想要的业务逻辑了。