异步处理-Promise的串联

1,006 阅读4分钟

应用场景

当后续的Promise需要用到之前的Promise的处理结果时,需要Promise的串联。

如何使用Promise的串联?

举例说明:获取李华所在班级的老师的信息。

思路:

  1. 根据李华的id获取所在班级的id;
  2. 根据班级id获取老师的id;
  3. 根据老师的id获取老师的信息。

ES5的写法造成回调地狱。

Promise对象中,无论是then方法还是catch方法,它们都具有返回值,返回的是一个全新的Promise对象,它的状态满足下面的规则:

  1. 如果当前的Promise是未决状态,得到的新的Promise是挂起状态;

    第一个Promise服务器还没有数据返回,还没有结果,所以得到的新的Promise也是挂起状态

    // 获取学生数据
    const pro = ajax({ 
        // 挂起状态
        url: "./data/students.json"
    })
    const pro2 = pro.then(resp =>{
        // 挂起状态
        
        
    })
    
    console.log(pro2) //  Promise pedding
    
  2. 如果当前的Promise是已决状态,会运行相应的后续处理函数;并将后续处理函数的结果(返回值)作为resolved状态数据,应用到新的Promise中;如果后续处理函数发生错误,则把返回值作为rejected状态数据,应用到新的Promise中。

    // 获取学生数据
    const pro = ajax({ 
        url: "./data/students.json"
    })
    const pro2 = pro.then(resp => {
        for (let i = 0; i < resp.length; i++) {
            if (resp[i].name === "李华") {
                return resp[i].classId; //班级id
            }
        }
    })
    console.log(pro2); // 新的Promise
    

    540b7a8c8e0b2e53332ddb8650914d1c.png

    分析:

    1. 当pro.then()还没有运行里面的函数,此时的pro是未决状态
    2. 一旦pro.then()运行了里面的函数,pro2变成了已决状态。 该已决状态是resolved还是rejected,取决于pro.then()里有没有错误;没有错误,就是resolved,有错误,就是rejected。 举例说明:classId找到之后再进行后续处理
    3. 状态数据就是pro.then()的返回值

    后续的Promise一定会等到前面的Promise有了后续处理结果后,才会变成已决状态

    举例说明:

    const pro1 = new Promise((resolve, reject) => {
        resolve(1);
    })
    const pro2 = pro1.then(result => result * 2);
    console.log(pro2);
    // pro2类型:Promise对象
    // pro2状态:pending
    

    分析:

    1. 虽然pro1是已决状态
    2. 但是,Promise的then里面的函数是异步调用的,要到事件队列里面去
    3. 所以,运行到console.log(pro2);的时候,pro1的then里面的函数还没有调用。console.log(pro2);是同步代码。
    4. 既然没有调用,那pro1的后续处理还没有处理。虽然pro1的状态变成了resolved,但是还没有进行后续处理。
    5. 还没有进行后续处理,就还没有一个已处理的结果
    6. 没有已处理的结果,pro2就还在等
    7. pro2的状态一定是前面一个Promise已经处理得到一个结果,才会变成已决

    例子一:打印输出结果

    const pro1 = new Promise((resolve, reject) => {
        resolve(1);
    })
    const pro2 = pro1.then(result => result * 2);
    pro2.then(result => console.log(result), error => console.log(error))        
    // 输出?  2
    

    分析:

    1. pro1一定变成已决 resolved状态;
    2. 函数result => result * 2一定会执行,执行完返回结果;
    3. 有了结果后,pro2才变成已决;
    4. pro2变成已决之后,返回的结果result * 2是状态数据;
    5. 最后执行pro2的console.log(result).

    例子二:打印输出结果

    const pro1 = new Promise((resolve, reject) => {
        throw 1;
    })
    const pro2 = pro1.then(result => result * 2, error => error * 3);
    pro2.then(result => console.log(result * 2), error => console.log(error * 3))
    // 输出?  3
    

    分析:

    1. new Promise((resolve, reject) => { throw 1; }) 会把状态推向reject;
    2. 所以,运行函数error => error * 3,得到一个结果3;
    3. pro2变成已决,接着运行then;
    4. 因为上一个运行没有错误,所以运行result => console.log(result * 2);
    5. 如果上一个处理有错误,则运行error => console.log(error * 3)

判断题:

then返回的Promise一开始一定是挂起状态?正确

分析: then里面的后续处理是异步的,等到同步代码执行完之后才会执行。既然一开始没出结果,那就一定是挂起状态。


小细节: 如果前面的Promise(pro1)的后续处理,返回的是一个Promise(pro2),则返回的新的Promise(pro3)的状态和后续处理返回的Promise(pro2)状态保持一致。(也就是下例中的pro3和pro2的状态保持一致。 )

const pro1 = new Promise((resolve,reject)=>{
    resolve(1);
})

const pro2 = new Promise((reject,resolve)=>{
    resolve(2);
})

const pro3 = pro1.then(result=>{
    return pro2;
})

分析:

  1. 按照之前的说法,pro2应该是pro3的状态数据
  2. 但是,由于返回的是Promise,所以,pro3的后续处理的状态数据和pro2的状态数据保持一致,即 2 。

修改代码:

const pro1 = new Promise((resolve, reject) => {
    resolve(1);
})

const pro2 = new Promise((reject, resolve) => {
    setTimeout(function () {
        resolve(2);
    }, 3000)
})

const pro3 = pro1.then(result => {
    return pro2;
})

pro3.then(result => {
    console.log(result === 2)
})
// pro2和pro3同时等三秒输出 2

简化代码:

const pro1 = new Promise((resolve, reject) => {
    resolve(1);
})

const pro2 = new Promise((reject, resolve) => {
    setTimeout(function () {
        resolve(2);
    }, 3000)
})

pro1.then(result => {
    console.log('结果出来了,得到的是一个Promise')
    return pro2;
}).then(result => {
    console.log(result)
}).then(result => {
    console.log(result)
})
// 输出 结果出来了,得到的是一个Promise
// 隔三秒输出 2
// 马上 输出 undefined

举例:获取李华所在班级的老师的信息。

思路:

根据李华的id获取所在班级的id; 根据班级id获取老师的id; 根据老师的id获取老师的信息。

const pro = ajax({
    url: "./data/students.json"
})
pro.then(resp => {
    for (let i = 0; i < resp.length; i++) {
        if (resp[i].name === "李华") {
            return resp[i].classId; //班级id
        }
    }
}).then(cid => {
    return ajax({
        url: "./data/classes.json?cid=" + cid
    }).then(cls => {
        for (let i = 0; i < cls.length; i++) {
            if (cls[i].id === cid) {
                return cls[i].teacherId;
            }
        }
    })
}).then(tid => {
    return ajax({
        url: "./data/teachers.json"
    }).then(ts => {
        for (let i = 0; i < ts.length; i++) {
            if (ts[i].id === tid) {
                return ts[i];
            }
        }
    })
}).then(teacher => {
    console.log(teacher);
})

举例: 垦哥心中有三个女神 有一天,垦哥决定向第一个女神表白,如果女神拒绝,则向第二个女神表白,直到所有的女神都拒绝,或有一个女神同意为止 用代码模拟上面的场景

function biaobai(god) {
    return new Promise(resolve => {
        console.log(`邓哥向${god}发出了表白短信`);
        setTimeout(() => {
            if (Math.random() < 0.3) {
                //女神同意拉
                resolve(true)
            } else {
                //resolve
                resolve(false);
            }
        }, 500);
    })
}
biaobai('女神1').then(resp => {
    if (resp) { // 女神1同意
        console.log('女神1同意了')
        return;
    } else {
        return biaobai('女神2');
    }
}).then(resp => { // 可能有三种情况
    if (resp === undefined) { //女神1已经成功
        return;
    } else if (resp) { // 女神2同意了
        console.log('女神2同意了')
        return;
    } else { // 女神2拒绝了,表白第三个女神
        biaobai('女神3');
    }

}).then(resp => {
    if(resp === undefined){ //  前面已经成功
        return;
    } else if(resp){ // 女神3同意了
        console.log('女神3同意了')
        return;
    } else {
        console.log('都被拒绝了')
    }
})

扩展以上代码

  1. 循环处理
  2. 设置变量,接收最新的Promise
const gods = ['女神1', '女神2', '女神3'];
let pro;
for (let i = 0; i < gods.length; i++) {
    if (i === 0) {
        pro = biaobai(gods[i])
    }
    pro = pro.then(resp => {
        if (resp === undefined) {
            return;
        } else if (resp) {
            console.log(`${gods[i]}同意了`)
            return;
        } else {
            console.log(`${gods[i]}拒绝了`)
            if (i < gods.length - 1) {
                return biaobai(gods[i + 1]);
            }

        }
    })
}

核心:后续处理也会返回Promise