Promise解决了什么问题?
Promise的问世
存在问题
在es6出来以前,存在这么一个问题。b函数的执行,要依赖a函数的执行,a函数不执行,b函数的执行就会报错,但是在上文我们讨论了事件循环机制(Event Loop)
,由于a函数是一个耗时代码(异步任务),这个任务会被挂起,因此底层会先执行b函数,等同步代码执行完毕之后再执行a函数的调用。因此我们不得不用到回调,把b函数的执行放到a函数里面(如第六行注释)
let data = null
function a (){
setTimeout(()=>{
data = {name: '张三'}
// b()
},1000)
}
function b(){
console.log(data.name+'xxx');
}
a()
b()
如果代码很少,这么写确实没问题,但是当我们的业务越来越复杂
,我们就不得不写成这样,这里我们还只是写了a~f函数,如果存在五百个五千个函数,这样一份代码岂不是回调地狱
?当你想修改这份代码你敢动这里面的逻辑吗?
- 嵌套过深,一旦出现问题难以排查。
function a(){
b();
}
function b(){
c();
}
function c(){
d();
}
function d(){
e();
}
function e(){
f();
}
function f(){
}
a()
为了解决这样的问题,官方打造了一个构造函数promise
promise
解决问题
举一个例子,先相亲,再结婚。
function xq() {
setTimeout(() => {
console.log('相亲成功');
}, 2000)
}
function merry() {
setTimeout(() => {
console.log('结婚成功');
}, 1000)
}
xq();
merry();
但是我们这么写,就会还没相亲,就结婚了。
function xq() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('相亲成功');
resolve('promise成功');
}, 2000)
})
}
function merry() {
setTimeout(() => {
console.log('结婚成功');
}, 1000)
}
xq().then(()=>{
merry();
})
- 在相亲函数中返回一个promise对象(像极了你做了一堆的承诺之后才有了下一步)
resolve('promise成功')
通常是与 Promise 相关的操作。它用于将 Promise 的状态从“未决”变为“已解决(成功)”,并传递一个值。- 拿到承诺对象才有后面的故事.then,接收一个回调函数,里面来调merry。
因此,假设你结婚之后想生一个baby,这个函数就该写成这样了
function xq() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('相亲成功');
resolve('xx相亲成功');
}, 2000)
})
}
function merry() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('结婚成功');
resolve('承诺成功');
}, 1000)
})
}
function baby() {
console.log('生下宝宝');
}
xq().then(() => {
merry().then(() => {
baby()
})
})
但是这么写,也是不断的嵌套,这样的代码看着也属实恶心,因此,我们可以写成这种链式的结构
function xq() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('相亲成功');
resolve('xx相亲成功');
}, 2000)
})
}
function merry() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('结婚成功');
resolve('承诺成功');
}, 1000)
})
}
function baby() {
console.log('生下宝宝');
}
xq()
.then(() => {
return merry()
})
.then(() => {
baby()
})
- 注意:我们要
把merry的执行结果给返回
,否则下一次.then就找不到依据了,就会让同步代码先执行,baby先诞生。
讲到这个地方,你已经知道了在我们许诺之后要有一个结果resolv('xx成功')\reject('xx失败')
,但是你可能就要开始好奇,这个里面接收的参数,会拿到哪里去。
xq()
.then((res) => {
console.log(res);
return merry()
})
.then((res2) => {
console.log(res2);
baby()
})
- resolve()接收的值,会由.then来接收
小伙伴要问了还有一个reject参数怎么回事?(失败的情况)
function a() {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log('a is ok');
reject('a')
}, 1000)
})
}
function b() {
console.log('b is ok')
}
a()
.then((res) => {
b()
})
.catch((err) => {
console.log(err);
})
- catch((err) => {}) err 是 promise 中 reject(xx) 出来的值
promise应用拓展,解析fetch操作
现在我们知道resolve传递的参数会给到then
,因此我们接下来做一个前后端融合小实战,深入理解js为哦我们封装的fetch操作到底是如何实现的
- 需求:点击button按钮,前端向后端接口发送请求,拿到数据,然后将数据渲染到页面上
- 获取数据getData()
- 渲染数据renderli()
- 获取电影列表接口:'xxx'已给,封装一个,实现向任意端口发送请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">请求数据</button>
<ul id="ul"></ul>
</body>
</html>
JavaScript
<script>
function getData(url) {
return new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
let movieList = JSON.parse(xhr.responseText).movieList
// console.log(movieList);
resolve(movieList)
}
}
})
}
function renderLi(arr) {
// 创建 li
arr.forEach(item => {
let li = document.createElement('li');
li.innerText = item.nm
document.getElementById('ul').appendChild(li);
});
}
document.getElementById('btn').addEventListener('click', () => {
getData('https://mock.mengxuegu.com/mock/65a91543c4cd67421b34c898/movie/movieList')
.then(res => {
renderLi(res)
})
})
</script>
- getData获取数据函数,创建请求实例,向端口发送请求并解析数据,由resolve传递结果。封装了getData函数,只需要给到url,就能获取数据。
- renderli渲染函数,拿到数据(数组),创建li,插入遍历数组得到的结果,并挂载到ul身上
- 为btn绑定监听事件,将获取到的结果给到resolve(),通过.then传递结果res给到渲染函数身上
接下来我们将我们自己封装的getData函数注释掉,直接用官方封装好的fetch看看效果
function renderLi(arr) {
// 创建 li
arr.forEach(item => {
let li = document.createElement('li');
li.innerText = item.nm
document.getElementById('ul').appendChild(li);
});
}
document.getElementById('btn').addEventListener('click', () => {
// getData('https://mock.mengxuegu.com/mock/65a91543c4cd67421b34c898/movie/movieList')
// .then(res => {
// renderLi(res)
// })
fetch('https://mock.mengxuegu.com/mock/65a91543c4cd67421b34c898/movie/movieList')
.then(res => {
return res.json()
})
.then(data => {
console.log(data);
renderLi(data.movieList)
})
})
- 猛然发现,效果一模一样,事实上,官方封装的fetch和我们打造的getData的逻辑是一样的,用promise,通过resolve传递参数,实现先获取数据再渲染页面。
小结
Promise为异步操作
提供了清晰的处理方式。通过 Promise,可以更优雅地管理异步任务的状态和结果,使得代码结构更清晰、逻辑更简洁。它有助于避免回调地狱
的出现,提升了代码的可读性和可维护性
。Promise 的使用让开发者能够更轻松地处理异步流程中的成功与失败
情况,实现了更好的错误处理机制
。
转载自:https://juejin.cn/post/7380637950381621286