大白話講解Promise

https://www.cnblogs.com/lvdabao/p/es6-promise-1.html 講得很好

一、鏈?zhǔn)讲僮鞯挠梅?、resolve() 的用法

    <script>
        console.dir(Promise);
        function fun0() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)0");
                    resolve('隨便什么數(shù)據(jù)0')
                }, 1000)
            })
            return p
        }
        function fun1() {
            var p1 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)1");
                    resolve('隨便什么數(shù)據(jù)1')
                }, 1000)
            })
            return p1
        }
        function fun2() {
            var p2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)2");
                    resolve('隨便什么數(shù)據(jù)2')
                }, 1000)
            })
            return p2
        }
        function fun3() {
            var p3 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)3-");
                    resolve('隨便什么數(shù)據(jù)3')
                }, 1000)
            })
            return p3
        }
        fun0().then((data) => {
            console.log(data);
            return fun1()
        }).then((data) => {
            console.log(data);
            return fun2()
        }).then((data) => {
            console.log(data+`結(jié)束`);
            // return fun3()
        })

    </script>

二、reject的用法

<script>
        function getNum() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    var num = Math.ceil(Math.random() * 10)     //生成1-10的隨機(jī)數(shù)
                    num <= 5 ? resolve(num) : reject('數(shù)字不符合')
                }, 500)
            })
            return p
        }
        getNum().then(
            (data) => {
                console.log('resolved');
                console.log(data);
            },
            //then的第二個(gè)參數(shù),用來指定reject的回調(diào)
            (reason, data) => {   //調(diào)用reject并傳遞一個(gè)參數(shù),作為失敗的原因。
                console.log('rejected');
                console.log(reason);
            }
        )

    </script>

有兩種結(jié)果:
狀態(tài)置為fullfiled

image.png

或者
狀態(tài)置為rejected
image.png

三、catch的用法

我們知道Promise對(duì)象實(shí)例可以共享 Promise 構(gòu)造函數(shù)上的then、catch、finally等方法,catch和then的第二個(gè)參數(shù)一樣,用來指定reject的回調(diào),用法是這樣:

        function getNum() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    var num = Math.ceil(Math.random() * 10)  ////生成1-10的隨機(jī)數(shù)
                    num <= 5 ? resolve(num) : reject('數(shù)字不符合')
                }, 500)
            })
            return p
        }
        getNum().then(
            (data) => {
                console.log('resolved');
                console.log(data);
            }

            // (reason, data) => {
            //     console.log('rejected');
            //     console.log(reason);
            // }
        ).catch(function (reason) {
            console.log('rejected');  //效果和寫在then的第二個(gè)參數(shù)里面一樣。
            console.log(reason);
        })

效果和寫在then的第二個(gè)參數(shù)里面一樣。不過它還有另外一個(gè)作用:在執(zhí)行resolve的回調(diào)(也就是上面then中的第一個(gè)參數(shù))時(shí),如果拋出異常了(代碼出錯(cuò)了),那么并不會(huì)報(bào)錯(cuò)卡死js,而是會(huì)進(jìn)到這個(gè)catch方法中。請(qǐng)看下面的代碼:

    <script>
        function getNum() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    var num = Math.ceil(Math.random() * 10)  ////生成1-10的隨機(jī)數(shù)
                    num <= 5 ? resolve(num) : reject('數(shù)字不符合')
                }, 500)
            })
            return p
        }
        getNum().then(
            (data) => {
                console.log('resolved');
                console.log(data);
                console.log(somedata); //此處的somedata未定義
            }

            // (reason, data) => {
            //     console.log('rejected');
            //     console.log(reason);
            // }
        ).catch(function (reason) { //效果和寫在then的第二個(gè)參數(shù)里面一樣。
            console.log('rejected');  
            console.log(reason);
        })

    </script>
image.png

也就是說進(jìn)到catch方法里面去了,而且把錯(cuò)誤原因傳到了reason參數(shù)中。即便是有錯(cuò)誤的代碼也不會(huì)報(bào)錯(cuò)了,這與我們的try/catch語句有相同的功能。

四、all的用法

Promise的all方法提供了并行執(zhí)行異步操作的能力,并且在所有異步操作執(zhí)行完后才執(zhí)行回調(diào)。我們?nèi)耘f使用上面定義好的fun0(), fun1(), fun2(), fun3()這四個(gè)函數(shù),看下面的例子:

  <script>
        console.dir(Promise);
        function fun0() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)0");
                    resolve('隨便什么數(shù)據(jù)0')
                }, 1000)
            })
            return p
        }
        function fun1() {
            var p1 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)1");
                    resolve('隨便什么數(shù)據(jù)1')
                }, 1000)
            })
            return p1
        }
        function fun2() {
            var p2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)2");
                    resolve('隨便什么數(shù)據(jù)2')
                }, 1000)
            })
            return p2
        }
        function fun3() {
            var p3 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)3-");
                    resolve('隨便什么數(shù)據(jù)3')
                }, 1000)
            })
            return p3
        }
        // fun0().then((data) => {
        //     console.log(data);
        //     return fun1()
        // }).then((data) => {
        //     console.log(data);
        //     return fun2()
        // }).then((data) => {
        //     console.log(data + `結(jié)束`);
        //     // return fun3()
        // })
        Promise.all([fun0(), fun1(), fun2(), fun3()]).then((results) => {
            console.log(results);
        })
    </script>

用Promise.all來執(zhí)行,all接收一個(gè)數(shù)組參數(shù),里面的值最終都算返回Promise對(duì)象。這樣,三個(gè)異步操作的并行執(zhí)行的,等到它們都執(zhí)行完后才會(huì)進(jìn)到then里面。那么,三個(gè)異步操作返回的數(shù)據(jù)哪里去了呢?都在then里面呢,all會(huì)把所有異步操作的結(jié)果放進(jìn)一個(gè)數(shù)組中傳給then,就是上面的results。所以上面代碼的輸出結(jié)果就是:


image.png

有了all,你就可以并行執(zhí)行多個(gè)異步操作,并且在一個(gè)回調(diào)中處理所有的返回?cái)?shù)據(jù),是不是很酷?有一個(gè)場景是很適合用這個(gè)的,一些游戲類的素材比較多的應(yīng)用,打開網(wǎng)頁時(shí),預(yù)先加載需要用到的各種資源如圖片、flash以及各種靜態(tài)文件。所有的都加載完后,我們?cè)龠M(jìn)行頁面的初始化。

五、race的用法

all方法的效果實(shí)際上是「誰跑的慢,以誰為準(zhǔn)執(zhí)行回調(diào)」,那么相對(duì)的就有另一個(gè)方法「誰跑的快,以誰為準(zhǔn)執(zhí)行回調(diào)」,這就是race方法,這個(gè)詞本來就是賽跑的意思。race的用法與all一樣,我們把上面fun0(), fun1(), fun2(), fun3()的延時(shí)改一下來看一下:

    <script>
        console.dir(Promise);
        function fun0() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)0");
                    resolve('隨便什么數(shù)據(jù)0')
                }, 1000)
            })
            return p
        }
        function fun1() {
            var p1 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)1");
                    resolve('隨便什么數(shù)據(jù)1')
                }, 2000)
            })
            return p1
        }
        function fun2() {
            var p2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)2");
                    resolve('隨便什么數(shù)據(jù)2')
                }, 3000)
            })
            return p2
        }
        function fun3() {
            var p3 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log("執(zhí)行完成任務(wù)3-");
                    resolve('隨便什么數(shù)據(jù)3')
                }, 4000)
            })
            return p3
        }
        // fun0().then((data) => {
        //     console.log(data);
        //     return fun1()
        // }).then((data) => {
        //     console.log(data);
        //     return fun2()
        // }).then((data) => {
        //     console.log(data + `結(jié)束`);
        //     // return fun3()
        // })
        Promise.race([fun0(), fun1(), fun2(), fun3()]).then((results) => {
            console.log(results);
        })
image.png

你猜對(duì)了嗎?不完全,是吧。在then里面的回調(diào)開始執(zhí)行時(shí),fun1(), fun2(), fun3()并沒有停止,仍舊再執(zhí)行。于是再過1秒后,輸出了他們結(jié)束的標(biāo)志。

這個(gè)race有什么用呢?使用場景還是很多的,比如我們可以用race給某個(gè)異步請(qǐng)求設(shè)置超時(shí)時(shí)間,并且在超時(shí)后執(zhí)行相應(yīng)的操作,代碼如下:


    <script>
        //請(qǐng)求某個(gè)圖片資源
        function requestImg() {
            var p = new Promise(function (resolve, reject) {
                var img = new Image();
                img.onload = function () {
                    resolve(img);
                }
                img.src = 'https://www.baidu.com/';
            });
            return p;
        }

        //延時(shí)函數(shù),用于給請(qǐng)求計(jì)時(shí)
        function timeout() {
            var p = new Promise(function (resolve, reject) {
                setTimeout(function () {
                    reject('圖片請(qǐng)求超時(shí)');
                }, 5000);
            });
            return p;
        }

        Promise
            .race([requestImg(), timeout()])
            .then(function (results) {
                console.log(results);
            })
            .catch(function (reason) {
                console.log(reason);
            });
    </script>

requestImg函數(shù)會(huì)異步請(qǐng)求一張圖片,我把地址寫為"xxxxxx",所以肯定是無法成功請(qǐng)求到的。timeout函數(shù)是一個(gè)延時(shí)5秒的異步操作。我們把這兩個(gè)返回Promise對(duì)象的函數(shù)放進(jìn)race,于是他倆就會(huì)賽跑,如果5秒之內(nèi)圖片請(qǐng)求成功了,那么遍進(jìn)入then方法,執(zhí)行正常的流程。如果5秒鐘圖片還未成功返回,那么timeout就跑贏了,則進(jìn)入catch,報(bào)出“圖片請(qǐng)求超時(shí)”的信息。運(yùn)行結(jié)果如下:


image.png

六、總結(jié)

ES6 Promise的內(nèi)容就這些嗎?是的,能用到的基本就這些。
我怎么還見過done、finally、success、fail等,這些是啥?這些并不在Promise標(biāo)準(zhǔn)中,而是我們自己實(shí)現(xiàn)的語法糖。

本文中所有異步操作均以setTimeout為例子,之所以不使用ajax是為了避免引起混淆,因?yàn)檎勂餫jax,很多人的第一反應(yīng)就是jquery的ajax,而jquery又有自己的Promise實(shí)現(xiàn)。如果你理解了原理,就知道使用setTimeout和使用ajax是一樣的意思。說起jquery,我不得不吐槽一句,jquery的Promise實(shí)現(xiàn)太過垃圾,各種語法糖把人都搞蒙了,我認(rèn)為Promise之所以沒有全面普及和jquery有很大的關(guān)系。后面我們會(huì)細(xì)講jquery。

關(guān)于Promise還有一些內(nèi)容是需要講的,限于篇幅,本文就只作ES6 Promise的講解,接下來還會(huì)有大白話講解系列:
Promise/A+規(guī)范
jquery中的Promise

敬請(qǐng)期待!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容