异步请求的处理方式
案例要求:模拟网络请求,通过请求结果来调用成功的回调函数/调用失败的回调函数
在没有使用promise之前,需要callback来调用(自己封装好并定义好名称,要使用的时候才能调用)
function requestData(url, successCallback, failureCallback) {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
// url传入的是aaa, 请求成功
if (url === "aaa") {
// 成功
let names = ["abc", "cba", "nba"]
successCallback(names)
} else { // 否则请求失败
// 失败
let error = "请求失败, url错误"
failureCallback(error)
}
}, 3000);
}
// 成功的回调函数
function successCallback(result) {
console.log("请求成功" + result);
}
// 失败的回调函数
function failureCallback(error) {
console.log(error);
}
requestData("aac", successCallback, failureCallback)
弊端:因为是自定义的回调函数的名称和方法,所以在开发使用的时候要查看源码知道名称和方法后才能调用函数,增加了工作量
更好的方案:统一名称 promise,并规范好所有的代码编写逻辑
Promise
Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。
简单使用:
function foo() {
// 传入的这个函数, 被称之为 executor
// > resolve: 回调函数, 在成功时, 回调resolve函数
// >reject: 回调函数, 在失败时, 回调reject函数
return new Promise((resolve, reject) => {
resolve("success message")
// reject("failture message")
})
}
const fooPromise = foo()
// 调用resolve(请求成功)会来到then方法
fooPromise.then((res) => {
console.log(res)
}
// 调用reject(请求失败)会来到catch方法
fooPromise.catch((err) => {
console.log(err)
})
用promise重构原来的代码去解决案例(异步请求处理):
function requestData(url,) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url === "aaa") {
// 成功
let names = ["abc", "cba", "nba"]
resolve(names)
} else {
// 失败
let errMessage = "请求失败, url错误"
reject(errMessage)
}
}, 3000);
})
}
// main.js
const promise = requestData("aaa")
promise.then((res) => {
console.log("请求成功:", res)
})
promise.catch((err) => {
console.log("请求失败:", err)
})
在node中then和catch分开会报错
promise中的then和catch可以合并,给then传入两个回调函数:
第一个回调函数, 会在Promise执行resolve函数时, 被回调
第二个回调函数, 会在Promise执行reject函数时, 被回调
const promise = requestData("aaa")
promise.then((res) => {
console.log("请求成功:", res)
}, (err) => {
console.log("请求失败:", err)
})
promise的三种状态
一个 Promise 必然处于以下几种状态之一:
- _待定(pending)_:初始状态,既没有被兑现,也没有被拒绝。(resolve函数和reject函数都没有被调用)
- _已兑现(fulfilled)_:意味着操作成功完成。(已调用resolve函数)
- _已拒绝(rejected)_:意味着操作失败。(已调用reject函数)
当resolve函数已经被调用时,promise的状态就被确定了,这时再去调用reject函数是没有效果的,反之亦然。
Promise 的链式调用
连续执行两个或者多个异步操作(一个promise包含着多个promise)
在上一个操作执行成功之后,开始下一个的操作,并带着上一步操作所返回的结果。我们可以通过创造一个 Promise 链来实现连续执行两个或者多个异步操作的需求。
一个promise:
new Promise((resolve, reject) => {
resolve('res message')
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
两个promise(当前promise的状态由传入promise决定):
const promise = new Promise((resolve, reject) => {
// resolve("aaaaaa")
reject("err message")
})
const newPromise = new Promise((resolve, reject) => {
resolve(Promise)
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
Promise对象方法
// 可以通过下面代码查看Promise有哪些对象方法
console.log(Object.getOwnPropertyDescriptors(Promise.prototype))
Promise.prototype.then()
简单使用:
const promise = new Promise((resolve, reject) => {
resolve()
})
promise.then(res => {
console.log('res:',res)
}) // 当上面调用resolve时会调用下面的回调
同一个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)
})
then方法传入的 "回调函数: 可以有返回值,返回值是新的Promise
- 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值
// 下面是一个链式调用
promise.then(res => {
return "aaaaaa" // 没有返回值默认返回undefined
}).then(res => {
console.log("res:", res)
return "bbbbbb"
})
上面代码中第二个then方法接收的值是第一个then方法返回的,且上面调用resolve是与第二个then方法无关的(注意:这里两个then方法所捕获的Promise不是同一个,第二个then方法捕获的是第一个then方法返回产生的一个的新Promise)
- 如果我们返回的是一个Promise,则后一个的then方法所捕获的Promise取决于前一个返回的Promise
promise.then(res => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111111)
}, 3000)
})
}).then(res => {
console.log("res:", res)
})
Promise.prototype.catch()
通过catch方法来传入错误(拒绝)捕获的回调函数
简单使用:
const promise = new Promise((resolve, reject) => {
reject()
})
promise.catch(err => {
console.log('err:',err)
})
注意:catch捕获顺序
- 当上面调用的是resolve时:
- 如果我们的catch方法是写在then方法之后的,当then方法返回的是一个普通值,那then方法和catch方法捕获的是同一个Promise。
- 但如果then方法返回的是一个新的Promise且调用了reject/throw Error,则这个then方法之后的catch方法优先捕获的是新的Promise
const promise = new Promise((resolve, reject) => {
resolve('111')
})
promise.then(res => {
return new Promise((resolve, reject) => {
reject("then rejected status")
})
}).catch(err => {
console.log("err:", err) // err: then rejected status
})
- 当上面调用的是reject时:
- catch方法捕获的是与then方法同一个Promise,无论then方法返回的是什么值
const promise = new Promise((resolve, reject) => {
reject('111')
})
promise.then(res => {
return new Promise((resolve, reject) => {
reject("then rejected status")
})
}).catch(err => {
console.log("err:", err) // err: 111
})
catch返回值:返回值也是新的Promise
- 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值(与then方法一样)
- 如果我们希望后续继续执行catch,那么需要抛出一个异常
Catch 的后续链式操作
在回调的时候抛出错误之后想要再次进行新的操作则可以使用catch来实现
new Promise((resolve, reject) => {
console.log('开始回调-------');
resolve();
})
.then(() => {
throw new Error('error message');
console.log('aaa');
})
.catch(() => {
console.log('bbb');
})
.then(() => {
console.log('ccc');
});
在还没有开始执行之前,VSCode就已经检测出第一个then里面抛出错误之后的代码不会执行:
执行输出:
总结:当出现失败的情况时,使用catch()中断(在catch回调和抛出错误的then回调之间的then回调也是不会执行的,catch回调之后的then回调可以执行)
Promise.prototype.finally()
finally() 方法返回一个 Promise。在 promise 结束时, 无论结果是 fulfilled 或者是 rejected, 都会执行指定的回调函数。
这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在 then() 和 catch() 中各写一次的情况
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")
})
Promise类方法
直接通过类名调用的方法
Promise.resolve
将普通对象转成Promise对象并调用resolve
- 传入普通的值
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.reject
将普通对象转成Promise对象并调用reject
const promise = Promise.reject("rejected message")
// 相当于
const promise2 = new Promsie((resolve, reject) => {
reject("rejected message")
})
Promise.all
所有的Promise都变成fulfilled状态时(所有Promise都调用resolve之后), 再拿到结果
// 创建多个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);
})
Promise.all([p2, p1, p3, "aaaa"]).then(res => {
console.log(res) // [22222,11111,33333,'aaaa']
})
但是在拿到所有结果之前, 有一个promise变成了rejected状态, 那么整个promise是rejected状态(Promise.all方法会被中断)
Promise.allSettled
所有的Promise都有结果(无论是fulfilled状态还是rejected状态)之后, 再拿到结果
// 创建多个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.race
只要有一个Promise变成fulfilled状态, 那么就结束,但有一个Promise状态先变为rejected,则结束
Promise.any
等到至少有一个Promise变成fulfilled状态, 才就结束,不管有没有Promise状态先变为rejected。如果全为rejected状态则等到全部执行完才结束并执行catch方法(全部rejected状态的错误信息)