✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
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应该先懂回调,简单理解回调函数能够实现异步编程(可以控制函数调用顺序)。紧接着你应该知道回调地狱,或者函数瀑布,就类似如下的代码:
<!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
<!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方法。
案例:
<!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
}) .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>
简洁案例:
<!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将响应结果返回,拿到外面管理,就避免了地狱回调的出现