1
不想了解过程,请直接划到最后.看最终效果

更多内容欢迎访问我的博客:林除夕

什么是回调地狱

通俗的说就是回调函数嵌套回调函数的问题,如下

// 声明一个函数
function foo(data, callback) {
    data++;
    callback(data);
}

// 回调地狱
foo(1, function(data) {
    foo(data, function(data) {
        foo(data, function(data){
            foo(data, function(data){
                foo(data, function(data){
                    foo(data, function(data){
                        console.log(data);// 7
                    })
                })
            })
        })
    });
});

怎么样是不是很刺激,稍微复杂一点,保证你看到头晕眼花。

解决回调地狱的基本思路


将这种嵌套式的调用改造为串联式,那么下面我们就来看看怎么解决.

解决办法===>Promise


1.什么是Promise

  1. 是一个构造函数:我们通过输出window对象得到相关信息
  2. 其中的关键方法

    • then: 用来指定接下来要执行的函数(成功的回调函数,失败的回调函数)
    • resolve: 成功之后的回调函数 ===> 必须要传的
    • reject: 失败的回调函数 ===> 不是必须传的
  3. 表示一个异步操作: 当实例被创建时就表示一个具体异步操作如果不传入执行函数,那么它只是在形式上是异步操作,但是没有任何的异步实操如下

    // 创建一个形式上的异步操作 ===> 没有任何具体的异步操作(例如ajax,jsonp等),我们知道它是异步操作但是不知道它究竟在操作什么
    var p = new Promise();
    
    // 创建一个实际的异步操作
    var p = new Promise(function(){
        // 在这里执行一个异步操作,例如:ajax请求
    })
  4. 实例被创建时,传入的函数会被立即执行: 所以一般都先将实例化过程封装在函数中,这样才能在需要的时候再执行

2.如何使用

  1. 通过.then指定回调函数(包括成功和失败),如下:

    // 为了防止我们的异步操作函数在Promise实例化过程中被执行,将其封装在一个函数中,在对应的时机去使用它
    function sendAjax() {
        // 创建并返回一个包含实际的异步操作的Promise对象
        return new Promise(function(resolve,reject) {
            // 在这里执行一个异步操作,例如:ajax请求
    
            if('ajax请求出现错误') return reject('错误内容')
            resolve('成功之后需要用到的数据或内容')
        });
    }
    
    // 接收实例对象
    var p = sendAjax()
    
    // 通过.then方法指定成功和失败的回调
    p.then(function(data){
        // 默认第一个是成功的回调
    }, function(err){
        // 默认第二个是失败的回调
    })
  2. 同执行过程如下: 在异步操作的等待时间内完成后续的处理函数的注册(因为异步操作受网络和数据大小的限制一般时间都较长,而后续注册执行函数因为都是内存内直接操作且数据量很小注册时间几乎可以忽略不计)

    st=>start: 开始
    e=>end: 结束
    op1=>operation: 调用函数实例化对象
    op2=>operation: 发起异步操作请求
    op3=>operation: 注册回调函数p.then
    op4=>operation: 完成异步请求
    op5=>operation: 调用回调函数
    op6=>operation: 异步请求是否完成
    cont=>condition: yes or no?
    st->op1->op2->op6->cont
    cont(no)->op3->op5
    cont(yes)->op5->e

3.最终效果

注意:

在调用.then的时候,成功的回调(resolve)是必须的,失败的回调(reject)可以不传递
// 为了防止我们的异步操作函数在Promise实例化过程中的执行,将其封装在一个函数中
function sendAjax(data) {
    // 创建并返回一个包含实际的异步操作的Promise对象
    return new Promise(function(resolve,reject) {
        // 在这里执行一个异步操作,例如:ajax请求

        if('ajax请求出现错误') return reject('错误内容')
        resolve('成功之后需要用到的数据或内容')
    });
}

// 接收实例对象
var p = sendAjax(data)
// 通过.then方法指定成功和失败的回调
p.then(function(data){
    // 默认第一个是成功的回调
}, function(err){
    // 默认第二个是失败的回调
})

// ========================
// 串联解决回调地狱问题
sendAjax(data)
    .then(function(newData1){
    // newData就是异步操作得到的数据或内容
    // 这里用来放成功之后如何处理

    // 数据处理完成后如果还需要执行嵌套的类似操作
    return sendAjax(newData2)
})
    .then(function(newData3){
    // newData就是异步操作得到的数据或内容
    // 这里用来放成功之后如何处理

    // 数据处理完成后如果还需要执行嵌套的类似操作
    return sendAjax(newData4)
})
    .then....
    .then(function(result){
        // 结束数据嵌套处理,输出数据
        console.log(result)
    })

4.捕获异常或错误

有两种情况,一种会阻止后续执行,一种不会

  1. 不阻止后续执行的捕获,提供一个失败的回调函数
sendAjax(data)
    .then(function(newData1){
        // newData就是异步操作得到的数据或内容
        // 这里用来放成功之后如何处理

        // 数据处理完成后如果还需要执行嵌套的类似操作
        return sendAjax(newData2)
    },function(err){
        // 在这里执行一个处理失败信息的操作
    
        // 处理完成后返回下一个要处理的对象,这样就不会阻止后续操作
        return sendAjax(newData2)
    })
    .then(function(newData3){
        // newData就是异步操作得到的数据或内容
        // 这里用来放成功之后如何处理

        // 数据处理完成后如果还需要执行嵌套的类似操作
        return sendAjax(newData4)
    })
  1. 阻止后续执行的捕获
sendAjax(data)
    .then(function(newData1){
        // newData就是异步操作得到的数据或内容
        // 这里用来放成功之后如何处理

        // 数据处理完成后如果还需要执行嵌套的类似操作
        return sendAjax(newData2)
    })
    .then(function(newData3){
        ...
    })
    .catch(function(err){
        // 输出错误信息
        // 这种方式捕获异常和错误,只要前面任何一个.then出现问题都会终止整个执行链,然后抛出异常
    })

林除夕
43 声望3 粉丝

前端在学,欢迎讨论。如有项目随时用空。