likes
comments
collection
share

Promise解决了什么问题?

作者站长头像
站长
· 阅读数 53

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();
})
  1. 在相亲函数中返回一个promise对象(像极了你做了一堆的承诺之后才有了下一步)
  2. resolve('promise成功')通常是与 Promise 相关的操作。它用于将 Promise 的状态从“未决”变为“已解决(成功)”,并传递一个值。
  3. 拿到承诺对象才有后面的故事.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>
  1. getData获取数据函数,创建请求实例,向端口发送请求并解析数据,由resolve传递结果。封装了getData函数,只需要给到url,就能获取数据。
  2. renderli渲染函数,拿到数据(数组),创建li,插入遍历数组得到的结果,并挂载到ul身上
  3. 为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)
                })


        })

Promise解决了什么问题?

  • 猛然发现,效果一模一样,事实上,官方封装的fetch和我们打造的getData的逻辑是一样的,用promise,通过resolve传递参数,实现先获取数据再渲染页面。

小结

Promise为异步操作提供了清晰的处理方式。通过 Promise,可以更优雅地管理异步任务的状态和结果,使得代码结构更清晰、逻辑更简洁。它有助于避免回调地狱的出现,提升了代码的可读性和可维护性。Promise 的使用让开发者能够更轻松地处理异步流程中的成功与失败情况,实现了更好的错误处理机制

转载自:https://juejin.cn/post/7380637950381621286
评论
请登录