如何去解决回调地狱的问题 进来瞧瞧 走过路过 不要错过

151 阅读2分钟

回调地狱

代码示例:

这个是我写的一个简单的后台接口

// 静态资源托管,直接访问public/api.html

const express = require('express')
const app = express()
var cors = require('cors')

// 处理跨域请求
app.use(cors())

app.use( express.static('public') )
// 接口1 get
app.get('/get', (req, res) => {
  res.send([{id:1, name:'体育'}])
})

app.get('/getCategory', (req, res) => {
  if(req.query.id==1){
    res.send([{id:12, name:'NBA新闻'}, {id:13, name:'足球新闻'}])
  }else {
    res.send([{id:10, name:'羽毛球'}])
  }
})
app.get('/getNews', (req, res) => {
  if(req.query.id==12){
    res.send([{id:1, name:'谁能得到总冠军'}, {id:2, name:'湖人已经不行了么?'}])
  }else {
    res.send([])
  }
})
app.listen(3000)

这个是我写的前端代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>
        function getData({url,success}) {
            const xhr = new XMLHttpRequest()
            xhr.open('get', url)
            xhr.onreadystatechange = function (){
                if(this.readyState === 4){
                    // 加载完成
                    const d = JSON.parse(xhr.responseText);
                    success(d)
                }
            }
            xhr.send()
        }

        function getDataPromise (url) {
            return new Promise((resolve,reject) => {
                const xhr = new XMLHttpRequest()
                xhr.open('get', url)
                xhr.onreadystatechange = function (){
                    if(this.readyState === 4){
                        // 加载完成
                        const d = JSON.parse(xhr.responseText);
                        resolve(d)
                    }
                }
                xhr.send()
            })
        }


  
        getData({
            url:'http://localhost:3000/get',
            success: (data)=>{
                getData({
                    url:'http://localhost:3000/getCategory?id=1',
                    success: data => {
                        console.log(data)
                        getData({
                            url:'http://localhost:3000/getNews?id='+data[0].id,
                            success: data => {
                                console.log(data)
                        }
                    })
                }
            })
        }})

        getDataPromise('http://localhost:3000/get').then(data=>{
            return getDataPromise('http://localhost:3000/getCategory?id='+data[0].id)
        }).then(data => {
            return getDataPromise('http://localhost:3000/getNews?id='+data[0].id)
        }).then(data => {
            console.log(data)
        })

        async function get() {
            const res1 = await getDataPromise('http://localhost:3000/get')
            const res2 = await getDataPromise('http://localhost:3000/getCategory?id='+res1[0].id)
            const res3 = await getDataPromise('http://localhost:3000/getNews?id='+res2[0].id)
            console.log(res3)
        } 

        get()

    </script>
</body>
</html>

接下来讲讲如何解决

用promise来优化代码

const f = (filepath) => {
    const p = new Promise((resolve, reject)=>{
        // 这里写具体的代码,并在某个恰当的时机去调用resolve和reject函数。
        fs.readFile(filepath,'utf-8',(err, data)=>{
            if(err) {
                // console.log(err);
                reject(err)
            } else {
                resolve(data)
                // console.log(data)
            }
        })
    })
    return p
}
let str = ""
f('./1.txt').then(str1 => {
    console.log(str1)
    str+=str1
    return f('./2.txt')
}).then((str2)=>{
    str+=str2
    console.log(str2)
    return f('./3.txt')
}).then(str3=>{
    str+=str3
    console.log(str3)
    console.log(str)
})

async-await语法

async,await 是es7中新增的语法,用来进一步改进异步代码的写法,是promise升级版

async

async函数返回一个 Promise 对象。

async函数内部return语句返回的值是Promise 对象的值

function f1 () {
  return 1
}
async function f2 () {
  return 1
}
async function f3 () {}
const r1 = f1()
const r2 = f2()
const r3 = f3()
console.log(r1) // 1
console.log(r2) // Promise, resolved(1)
console.log(r3) // Promise, resolved(undefined)
r2.then(res => { console.log(res) })
r3.then(res => { console.log(res) })

await 命令

await的外层函数必须有一个async.

正常情况下,await命令后面是一个 Promise 对象,返回该promise的值。如果不是 Promise 对象,就直接返回对应的值。

<script>
    async function f() {
        const p = new Promise((resolve,reject)=>{
            resolve(100)
        })
        const a = 1
    
        // await 等待promise的状态变化完成(pending->resolved, pending->rejected)
        // 取出promise的值
        const b = await p 

        console.log(a,b)
    }

    f()
</script>

image.png

async函数内部的执行流程

在执行async函数(设名为asyncF)时,进入函数内部:

  按序执行同步代码  
  遇到await,跳出asyncF函数,  继续执行后续代码。

当await后的异步代码执行完成之后,接着执行asyncF中的后续代码。

const p1 = new Promise((resolve, reject) => {
  setTimeout(()=>resolve(100), 3000)
})

async function f () {
  console.log('开始执行f')
  var a = await p1
  console.log('p1完成了')
  console.log(a)
  return 1
}

console.log('主函数运行...')
const res = f()
console.log('f()完成,返回值是', res)
res.then(res => { console.log(res) })

执行结果是:

主函数运行...
开始执行f
f()完成,返回值是 Promise { <pending> }
----此处等了3s----
p1完成了
100
1

回调地狱-用async await优化

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        function getData({ url, success }) {
            const xhr = new XMLHttpRequest()
            xhr.open('get', url)
            xhr.onreadystatechange = function () {
                if (this.readyState === 4) {
                    // 加载完成
                    const d = JSON.parse(xhr.responseText);
                    success(d)
                }
            }
            xhr.send()
        }


        function getDataPromise(url) {
            var p = new Promise((resolve, reject) => {
                // 具体的异步代码
                // 恰当的时机,去调用resolve,reject
                const xhr = new XMLHttpRequest()
                xhr.open('get', url)
                xhr.onreadystatechange = function () {
                    if (this.readyState === 4) {
                        // 加载完成
                        const d = JSON.parse(xhr.responseText);
                        resolve(d)
                    }
                }
                xhr.send()
            })
            return p
        }


        // 回调地狱 问题 无限套娃
        // getData({
        //     url:'http://localhost:3000/get',
        //     success: (data)=>{
        //       getData({
        //             url:'http://localhost:3000/getCategory?id='+data[0].id,
        //             success: data => {
        //               getData({
        //                     url:'http://localhost:3000/getNews?id='+data[0].id,
        //                     success: data => {
        //                         console.log(data)
        //                 }
        //             })
        //         }
        //     })
        // }}) 

        //  用async await 优化
        async function fn() {
            const res = await getDataPromise('http://localhost:3000/get')
            console.log('第1个请求', res)
            const res1 = await getDataPromise('http://localhost:3000/getCategory?id=' + res[0].id)
            console.log('第2个请求', res1)
            const res2 = await getDataPromise('http://localhost:3000/getNews?id=' + res1[0].id)
            console.log('第3个请求', res2)
        }
        fn()
    </script>
</body>

</html>