promise使用详解

号外号外:本篇文章主要使用了ES6箭头函数,如果不熟悉的同学建议看一看。嘻嘻

开篇之前先理清下面几个问题

Promise的构造函数参数可以传递什么类型?除了使用Promise构造函数进行链式调用外,Promise还有什么方式进行链式调用吗?

Promise.resolve()的参数可以传递什么类型的数据?不同类型的数据产生怎样的结果?

Promise的构造函数中调用resolve()/reject()这样做的目的是什么?

Promise的then方法是做什么的?

如何终止链式调用?(这个场景会在什么时候遇到)

如何把promise的代码转化为async await的调用方式

进阶:手写Promise(哈哈 是不是有点招人恨的样子...)

 

从第一个问题开始:Promise的构造函数参数必须是函数,函数有两个参数一个是resolve,一个是reject。除了Promise构造函数进行链式调用外,Promise.resolve()也可以进行链式调用,不过他的参数多数是基本数据类型会进行值穿透。

第二个问题:Promise.resolve()的参数是基本数据类型,会导致值传递

第三个问题:Promise的构造函数中调用resolve()/reject()这样做的目的是触发then的回调函数(resolve调用成功的回调,reject调用失败的回调)并且修改Promise的状态。状态有pending 转化为fulfilled或者rejected。

第四个问题:Promise的then方法可以返回上一个Promise的回到结果,也可以return一个新的Promise,从而实现链式调用

第五个问题:then返回一个空的Promise对象即可

第六个问题:

第七个问题:

为什么使用promise

解决回调地狱的代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象 。其次promise可以支持多个并发的请求,获取并发请求中的数据

 

常规回调方式实现:

setTimeout(() => {
    console.log('1');
    setTimeout(() => {
        console.log('2');
        setTimeout(() => {
            console.log('3');
            setTimeout(() => {
                console.log('4');
            }, 1000);
        }, 1000);
    }, 1000);
}, 1000);

promise方式实现:

function getStr1() {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('1');
        }, 1000);
    });
}
function getStr2() {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('2');
        }, 1000);
    });
}
function getStr3() {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('3');
        }, 1000);
    });
}
function getStr4() {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('4');
        }, 1000);
    });
}
getStr1().then((data) => {
    console.log(data);
    return getStr2();
}).then((data) => {
    console.log(data);
    return getStr3();
}).then((data) => {
    console.log(data);
    return getStr4();
}).then((data) => {
    console.log(data);
})

如果我们使用Promise去实现这个效果,虽然代码量不会减少,甚至更多,但是却大大增强了其可读性和可维护性

解密Promise核心

通过上面几个问题能感受到Promise更像一个保存状态的魔法罐,只要状态改变就可以就能一直传递下去,并且状态持久的保存在自己身上,随时都可以then函数的调用。其次状态是不可逆的,只能从pending 转化为fulfilled或者rejected。总结一句话就是状态就是Promise的灵魂所在,状态使用resolve或者reject管理的。接下来通过几个例子来理解Promise

例一:

首先需要知道Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的,方便下面工作进行

        let promise = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('success')
            }, 3000)
        })
        promise.then((data) => {
            console.log(data)
            console.log('resolved===', promise)
        })
        console.log('pengding====', promise)

输出结果:

image.png

如果我这里把resolve('success')注释掉会发生什么事情呢?有的同学会说then函数不再往下执行,恭喜你答对了,给你一个大大的赞!!!

image.png

就像上面七个问题中提到过的一样,resolve触发Promise状态的改变,然后触发then回调函数的调用,如果注释resolve('success'),那么整个链路就暂停了。是不是瞬间感受到状态是如何支撑Promise 运转的了。接下来再看看Promise 具有状态缓存的特性,国际惯例还是用例子引出我们的理论

        let promise = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('success')
            }, 3000)
        })
        promise.then((data) => {
            console.log("data1===" + data)
            console.log('resolved===', promise)
        })
        promise.then((data) => {
            console.log("data2===" + data)
            console.log('resolved===', promise)
        })
        promise.then((data) => {
            console.log("data3===" + data)
            console.log('resolved===', promise)
        })
        console.log('pengding====', promise)

promise使用详解_第1张图片

只要状态从pending 转化为resolved或者rejected之后,不管我们什么时候调用,都能拿到回调结果,但是这样做有什么好处呢?

回调函数

我们花费了大半篇文稿讲述Promise的状态管理,目的是什么?总不能做事情没目的吧!作为名侦探柯南的黑粉我要出来指证!!!目的就是触发then函数拿到回调结果,然后就可以为所欲为了。接下来就看看怎么拿到回调函数的

        let promise = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('success')
                // reject("fail")
            }, 1000)
        }).then(resolved => {
            console.log("resolved===" + resolved)
        }, rejected => {
            console.log("rejected===" + rejected)
        })
        console.log('pengding====', promise)

then函数传入两个参数,第一个参数代表成功的回调,第二个参数代表失败的回调,注意参数使用函数的形式,如果参数使用基本数据类型就会出现值穿透,小伙伴后果自负哟!!!函数的参数就是我们的回调结果,是不是还是以往的那么稳妥。对于没有成功回调的我们可以使用null代替,例子

        let promise = new Promise((resolve, reject) => {
            setTimeout(() => {
                // resolve('success')
                reject("fail")
            }, 1000)
        }).then(null, rejected => {
            console.log("rejected===" + rejected)
            return new Promise((resolve, reject) => {
                reject(100)
            })
        }).then(null,reject => {
            console.log("=====" + reject)
        })

对于then的参数期望是函数,传入非函数则会发生值穿透。

        Promise.resolve(1)
            .then(2)
            .then("3")
            .then(data => { console.log(data) })

image.png

链式调用

既然我们已经知道如何通过改变Promise的状态然后拿到回调结果,那我们就很容易实现链式调用了。这里就不卖关子了,我们让then回调函数返回一个新的Promise,这样就实现了链式调用,是不是很机智!!!至此那个男孩从此不再那么天真。。。一切没有使用案例的理论都是耍流氓,这里问题不大,不就是一个案例吗???哭笑脸

        let peopleObj = { name: "taowuhua", age: 18 }
        let promise = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('success')
            }, 100)
        }).then(resolved => {
            console.log("第一次链式调用结果" + resolved)
            return new Promise((resolve, reject) => {
                resolve(JSON.stringify(peopleObj))
            })
        }).then(resolved => {
            console.log("第二次链式调用结果" + resolved)
            return new Promise((resolve, reject) => {
                reject("2")
            })
        }, rejected => {
        })

image.png

一切都是那么的美好,任何人都不是 一帆风顺总会遇到挫折程序也不例外,中途遇到异常怎么办?怎么办?怎么办?我好害怕!!!这个时候catch从天而降,一切又回到原来的状态,不再害怕不再担心

        let promise = new Promise((resolve, reject) => {
            console.log(taowuhua)
        }).then(resolved => {
            return new Promise((resolve, reject) => {
                resolve(1)
            })
        }).catch(rejected => {
            console.log(rejected)
        })

image.png

到此链式调用就算说拜拜的时候到了。

 

then中 return 一个 error 对象并不会抛出错误,所以不会被后续的 catch捕获

        let promise = new Promise((resolve, reject) => {
            resolve("taowuhua")
        }).then(resolved => {
            return new Error("总是抓不住那颗受伤的心...")
        }).catch(rejected => {
            console.log(rejected)
        })
        let promise = new Promise((resolve, reject) => {
            resolve("taowuhua")
        }).then(resolved => {
            return new Error("总是抓不住那颗受伤的心...")
        }).then((resolve) => {
            console.log(resolve)
        }, (reject) => {
            console.log(reject)
        }).catch(rejected => {
            console.log(rejected)
        })

Promise常用Api

Promise.resolve

方法返回一个状态为resolved的promise实例

Promise.resolve('foo');//等价于如下
new Promise((resolve,reject)=>{
    resolve('foo');
})

Promise.reject

方法返回一个状态为rejected的promise实例

Promise.reject('foo');//等价于如下
new Promise((resolve,reject)=>{
    reject('foo');
})

Promise.all

function getStr1() {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('1');
        }, 1000);
    });
}
function getStr2() {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('2');
        }, 1000);
    });
}
function getStr3() {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('3');
        }, 1000);
    });
}
function getStr4() {
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('4');
        }, 1000);
    });
}

Promise.all([getStr1(), getStr2(), getStr3(), getStr4()]).then((values) => {
    console.log(values);
});

异步代码同步化

异步代码同步化并不是异步代码就变成了同步代码了,而是视觉上像同步代码在运行是的。

async await

建议移步这个链接:https://segmentfault.com/a/1190000007535316

async 用于申明一个 function 是异步的,async 函数返回 Promise为resolved状态的构造函数。而 await 用于等待一个异步方法执行完成

async函数有返回值(类似 Promise.resolve(x) )

async function testAsync() {
    return "hello async";
}
const result = testAsync();
console.log(result);

image.png

async function testAsync() {
    return "hello async";
}
testAsync().then(v => {
    console.log(v);    // 输出 hello async
});

async函数没有返回值(类似 Promise.resolve(undefined) )

async function testAsync() {
    
}
const result = testAsync();
console.log(result);

image.png

await等什么

如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果

 

假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:

/**
 * 传入参数 n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n + 200,这个值将用于下一步骤
 */
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}
function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}
function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}
function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

现在用 Promise 方式来实现这三个步骤的处理

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}
doIt();

经历了上面的磨难可以出去社会闯荡一下了,先从下面的两个小demo试试

        Promise.resolve(1)
            .then((res) => {
                return Promise.resolve(4)
            })
            .then(5)
            .then((res) => 2)
            .then(Promise.resolve(3))
            .then(data => {
                console.log(data)
            })
            Promise.resolve(Promise.resolve(3))
                    .then((res) => {
                        return Promise.resolve(4)
                    })
                    .then(5)
                    .then((res) => 2)
                    .then(Promise.resolve(3))
                    .then(data => {
                        console.log(data)
                    }) 

 

这里还有一部分面试题感兴趣的同学可以研究下。直通车:https://juejin.im/post/5a04066351882517c416715d

你可能感兴趣的:(前端)