✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
所属的专栏: 前端零基础教学,实战进阶
景天的主页: 景天科技苑
文章目录
Promise对象
- Promise 是异步编程 的一种解决方案,比传统的解决方案------回调函数 和事件------更合理和更强大。
它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。 - Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。
- Promise是一个构造函数,通过new来实例化,主要解决异步编程。
- 在 ES2015 中,Promise 诞生了。Promise 成功解决了回调函数 中嵌套调用 和错误跟踪、回调函数控制权等问题。
- 一个Promise对象有三种状态:pending(等待中)、fulfilled(已成功)或rejected(已失败)。当Promise对象处于pending状态时,它表示尚未完成,但可能会在未来某个时间完成。
new Promise(function(resolve,reject){
})
如果new Promise里面函数的参数 resolve和reject都没有执行,promise的状态就是pending状态,resolve执行了,就是fulfilled。reject执行了就是rejected状态
任何一门技术的出现,都是为了解决问题,他的出现是怎么把问题解决好的。我们重点要关注这一点
Promise出现之前,异步用的就是回调函数
(1)回调函数
了解promise应该先懂回调 ,简单理解回调函数 能够实现异步编程 (可以控制函数调用顺序)。紧接着你应该知道回调地狱,或者函数瀑布,就类似如下的代码:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
//
setTimeout(function () {
console.log("apple");
setTimeout(function () {
console.log("banana");
setTimeout(function () {
console.log("cherry");
}, 1000);
}, 2000);
}, 3000);
console.log("下一个操作")
</script>
</head>
<body>
</body>
</html>
setTimeout是异步操作,由于setTimeout里面的操作都需要等待,因此我们得预期效果是 先打印 下一个操作,然后3秒后打印 apple, 在等2秒后打印 banana, 再过1秒打印 cherry
我们看下控制台,结果如预期
我们可以把这三个操作想象成ajax向后端发送请求
进阶模拟更像ajax请求
apple请求回来,接着走后续操作
banana请求回来,接着走后续操作
结果如预期
目前这种效果是没问题的
但是,多的话,代码已经没法读了,地狱回调,已经无法直视
Promise 的出现就能够将上面嵌套格式的代码变成了较为有顺序的 从上到下风格的代码。
然后看下promise的异步实现
(2)promise基本语法
new Promise(function(resolve, reject) { });
- Promise构造函数 接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
- Promise 构造函数 只有一个参数 ,这个参数是一个函数,这个函数在构造之后会直接被异步 运行,所以我们称之为起始函数。
起始函数包含两个参数 resolve 和 reject。异步任务执行成功时调用resolve函数 返回结果,反之调用reject。 - Promise对象的then 方法用来接收处理成功 时响应的数据,catch 方法用来接收处理失败时相应的数据。
案例1
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function sayHi() {
var promise = new Promise(function (resolve, reject) {
var data = "hello world"
})
//将promise对象返回
//也可以直接return new Promise(function(){})
return promise
}
var p = sayHi()
console.log(p)
</script>
</head>
<body>
</body>
</html>
浏览器查看
p是个Promise对象,状态是pending状态。这是由于new Promise里面的函数参数resolve,reject方法都没执行。promise的状态就是pending
//返回的promise对象的then方法,里面的参数也是个函数。用来处理成功时的响应数据,catch用来处理失败时的响应数据
我们看到,new Promise里面的函数执行了,但是外面promise对象调用的then函数没有执行
这是因为,要执行then函数,需要new Promise里面执行函数的参数resolve方法
(3)then函数
Promise实例 生成以后,可以用then方法 分别指定resolved 状态和rejected 状态的回调函数**。**
Promise实例 具有then 方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。
前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
.then(function (){}, function (){});
如果初始函数里面没有指定resolve或者reject函数,那么 .then 函数是不会被调用的,因为之后状态变成了resolved或者rejected才会调用对应的回调函数。
放开resolve方法
then方法就执行了
并且,执行了resolve方法
promise的状态也变成了fulfilled
此时promiseResult为空
这是由于我们没把数据传递给resolve
当我们把数据传递给resolve
promiseResult就有值了
响应回来的值可以在then方法中,通过then里面函数的参数得到响应值
如果没有执行resolve或reject函数,p.then()永远也不会执行
如果上面的resolve方法没执行,但是reject方法执行了
此时promise的状态是rejected
并且会执行then里面的第二个函数,如果没有第二个函数,程序会报错
如果我们把then里面添加了第二个函数,程序会执行第二个函数
如果我们把resolve和reject都放开,程序按照从上往下执行,promise的状态只会改变一次
这是因为状态只会改变一次,之后不会更改的
reject在上面,所以只执行了reject的回调函数
promise执行流程:
- 构造函数中的输出执行是同步的,输出 apple,执行 resolve 函数,将 Promise 对象状态置为resolved,输出APPLE。
- 注册这个Promise对象的回调then函数。
- 宏任务继续,打印cherry,整个脚本执行完,stack 清空。
- eventloop 检查到 stack为空,再检查 microtask队列中是否有任务,发现了 Promise 对象的 then 回调函数产生的 microtask,推入stack,执行。输出apple banana,eventloop的列队为空,stack为空,脚本执行完毕。
(4)Promise链式应用
使用Promise可以更好地处理异步操作,例如网络请求,文件读取等。它避免了回调地狱(callback hell)的问题,使得代码更加容易理解和维护。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法 ,即then方法后面再调用另一个then方法。
案例:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
// 第一层:获取用户信息
function getUserInfo(userId) {
return new Promise((resolve, reject) => {
// 模拟异步操作,获取用户信息
setTimeout(() => {
const userInfo = {
id: userId,
name: "Jing hao",
email: "313572372@qq.com"
};
resolve(userInfo);
}, 1000);
});
}
// 第二层:获取用户订单列表
function getUserOrders(userId) {
return new Promise((resolve, reject) => {
// 模拟异步操作,获取用户订单列表
setTimeout(() => {
const orders = [
{id: 1, product: "Product A"},
{id: 2, product: "Product B"},
{id: 3, product: "Product C"}
];
resolve(orders);
}, 2000);
});
}
// 第三层:获取订单详情
function getOrderDetails(orderId) {
return new Promise((resolve, reject) => {
// 模拟异步操作,获取订单详情
setTimeout(() => {
const orderDetails = {
id: orderId,
status: "Delivered",
address: "123 Main St"
};
resolve(orderDetails);
}, 1500);
});
}
// 应用示例
const userId = 123;
//可以连续then,响应成功继续下一步操作
getUserInfo(userId)
.then(userInfo => {
console.log("User Info:", userInfo);
return getUserOrders(userInfo.id); //每个then里面返回新的promise函数。如果我们没有手动返回,也会返回的默认的新的Promise对象,其中Promisestate是fulfilled,PromiseResult是undefined
如下,p2的then里面没有写返回值,默认then函数会返回一个新的promise
html
})
.then(orders => {
console.log("User Orders:", orders);
const orderId = orders[0].id;
return getOrderDetails(orderId); //每个then里面返回新的promise函数
})
.then(orderDetails => {
console.log("Order Details:", orderDetails);
})
//响应失败的数据交给catch
.catch(error => {
console.error("Error:", error);
});
console.log("后续操作!!!")
</script>
</head>
<body>
</body>
</html>
简洁案例:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function A() {
var p = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("A请求")
var res = "A-data"
resolve(res)
}, 1000)
})
return p
}
function B() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("B请求")
var res = "B-data"
resolve(res)
}, 2000)
})
}
function C() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("C请求")
var res = "C-data"
resolve(res)
}, 3000)
})
}
function D() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("D请求")
var res = "D-data"
resolve(res)
}, 3000)
})
}
function E() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("E请求")
var res = "E-data"
resolve(res)
}, 3000)
})
}
/*var p1 = A()
var p2 = p1.then(function (res) {
console.log("A获取结果:", res)
return B()
})
var p3 = p2.then(function (res) {
console.log("B获取结果:", res)
return C()
})
p3.then(function (res) {
console.log("C获取结果:", res)
})*/
//链式操作
A().then(function (res) {
console.log("A获取结果:", res)
return B()
}).then(function (res) {
console.log("B获取结果:", res)
return C()
}).then(function (res) {
console.log("C获取结果:", res)
return D()
}).then(function (res) {
console.log("D获取结果:", res)
return E()
}).then(function (res) {
console.log("E获取结果:", res)
})
</script>
</head>
<body>
</body>
</html>
通过resolve或reject将响应结果返回,拿到外面管理,就避免了地狱回调的出现