從0到1掌握Promise(持續更新中)

2015年就已經出現的ES6新增語法中的Promise,我一直沒有時間好好研究。看了很多Promise的使用教程和原理解析,總以爲有些晦澀難懂或者不夠全面,看完也無法徒手寫一段使用Promise的代碼,因此決定本身從新學習,並在這裏簡單分享本身的學習筆記。html

Promise 是什麼?

根據Promises/A+定義的標準:前端

A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then method, which register callbacks to receive either a promise’s eventual value or the reason why the promise cannot be fulfilled.promise


promise對象表示異步操做的最終結果。使用promise的 主要方式是經過then方法,這個方法註冊回調以接收promise的最終值或者沒法fulfilled的緣由。這段話對於經驗豐富的人來講比較容易理解,對經驗稍少的人來講,可能就顯得過於抽象(固然,我翻譯得可能不夠準確)。初次接觸Promise,我建議能夠先不理會這些概念。
微信

Promise首先是一個構造函數,至關於靜態語言中的「類」,和Array、RegExp、Object是同一量級的數據類型(也就是函數)。網絡

那麼爲何要新增這一個構造函數呢?異步

到ECMAScript5這個版本中,js已經很強大了,React、Vue、Angular都是基於ECMAScript5開發的,前端開發已然面貌一新,爲何要畫蛇添足?async

先別急,讓咱們先假設一個場景。好比,咱們要在頁面上展現小豬佩琦一天的生活。其主要過程爲:醒來->起牀->穿衣服->刷牙->吃早餐->去學校->上課。最終的展現效果以下圖函數


咱們能夠放7個img標籤在html文件上,可是,通常狀況下,這樣作無疑是回到刀耕火燎的時代。那麼還有什麼辦法?通常來講,採用異步加載的方式是首選。經過new Image() 建立一個對象,拿到圖片,再渲染到頁面上。但是一共有7張圖片,並且它們須要按照固定的次序展現,那應該怎麼作呢?毫無疑問,最終的代碼會以下圖所示。學習

像這種層層嵌套的回調函數就是使人聞風喪膽的「回調地獄」。Promise就是爲解決這個問題而出現的。優化

也許上面的代碼看起來彷佛不是那麼恐怖,並且運行起來沒有問題。但請注意,這一段代碼的格式已經很規範,而且命名清晰明瞭。想象一下,若是你的程序出現了一個錯誤,你怎麼去調試?須要花多長時間?這段代碼你寫着寫着會不會混亂?固然,若是你有時間也沒問題,可是隔一個月再來看這段代碼,你看得懂嗎?或者這個項目讓別人接手,別人能看懂嗎?

固然,說得再多不如直接上代碼,請看下圖:


對比兩張圖,使用的Promise的代碼整整少了8行,並且清晰明瞭,很是簡潔、優化。這裏先不談兩段代碼執行過程的區別。Promise毫無疑問在可閱讀性上碾壓傳統的開發模式。


下面,咱們繼續深刻地談談Promise。根據文章開頭,Promises/A+的定義:

promise對象表示異步操做的最終結果

  • 什麼是異步操做?
    要理解什麼是異步操做,首先要明白代碼是怎樣被解釋而且執行的。
    好比下面這一段代碼的執行過程,我簡單地在旁邊標註了一下。

和異步操做相反地是同步操做,大部分狀況下,代碼地執行過程是同步地,也就是一行一行地往下執行,如上圖。

異步操做無非就是這三類:網絡請求(new XMLHttpRequest)、定時器(setTimeout、setInterval)和事件(addEventListener、元素的onclick等)。不過,在平常開發中,promise的使用場景通常都和網絡請求相關。但是咱們能夠看到promise原理剖析類的文章,Demo裏通常都會用到setTimout這個函數。爲何呢?固然是由於這樣就不用涉及到後臺開發了。


也許上面地代碼看上去像是同步地,由於代碼地執行過程是很快地。因此不妨把上面地1000改爲5000試試看。

  • 異步操做的結果指的是什麼?
    這裏能夠舉一個簡單的例子。好比,你給你的朋友發了個消息「請借我30塊買喜茶」,發完了以後,你繼續看書、打遊戲、敲代碼,不會什麼也不幹一直等着朋友的回覆。而朋友看到消息後直接轉帳給你或者回復「沒錢,不借」就是異步操做的「結果」,固然朋友也可能根本不理會你,這也是異步操做的「結果「。

new Promise(),至關於告訴Promise,我要給朋友發消息「請借我30塊買喜茶」,那麼Promise在實例化的過程當中,會執行這個任務。但是朋友究竟會何時回覆,回覆什麼,這個取決於朋友,沒有誰能夠保證。反正最終必定會有個結果的,不管是收到轉帳、拒絕或者不回覆。那麼咱們怎麼處理這些結果呢?若是朋友轉帳了,要怎麼作?若是朋友拒絕了,怎麼辦?若是朋友不回覆,怎麼處理?這就涉及到promise實例中的各類方法了。!

Promise的基本使用

應該如何使用Promise?下面咱們根據一個案例,嘗試使用它。

首先假設一個場景:

你忽然想喝喜茶,但是沒有錢,打算向朋友借錢。


  1. 你決定給一個朋友發微信消息「求求你借我30塊買喜茶,謝謝!」。
    用Promise應該怎麼作呢?Promise自己的意思是「承諾,諾言」,你能夠把它想像成一個小跟班兒,一個助理。給朋友發消息這種行爲咱們直接告訴它就能夠了,讓它替咱們完成。給朋友發送消息,其實就是發送一個網絡請求,以下圖的sendMsg函數。咱們把這個函數做爲參數傳給Promise。


    代碼執行到這兒,其實已經把網絡請求發送出去了。但是,小助理髮送了消息以後呢?它想知道本身的工做怎樣纔算結束,不論是成功地達到了你的指望,仍是失敗了,總而言之,都算結束。那麼你能夠告訴它,要是朋友回了消息,無論回的內容是什麼都算成功地結束了,要是你被對方刪除好友,雖然失敗,但也算是結束了。Promise本身提供了兩個函數(resolve 和 reject )給你分別對應這兩種狀況。能夠修改代碼至下圖,記住,new Promise()的時候傳入的參數只要是個函數就能夠了,不論是匿名函數體仍是存儲着函數地址的變量。


    小助理髮送了消息,本身也知道告終果,那麼知道了就算了嗎?固然不能,要是借到錢了,能夠順便讓小助理把茶買回來,要是發現本身被刪好友,可讓小助理順便也把那我的給刪了。這個時候就須要用到Promise實例(記住,是)的then方法了。讓小助理買茶,咱們用函數bugTea表示,讓小助理刪好友,咱們用函數deleteFriend表示。不過你可能不止一個助理,咱們要保證「借錢買茶」的是同一個助理,因此咱們首先須要用變量 promise 存儲new Promise() 的返回值,也就是一個Promise的實例。以下圖:


    不太小助理收到的回覆也不必定就是朋友的轉帳,朋友有可能回覆「怎麼那麼多人喝喜茶?」,這種狀況不可能讓小助理直接去買茶,因此能夠先看看朋友回覆的消息,要是朋友轉帳了,那就可讓小助理去買茶了。那麼在代碼實現上,咱們能夠在resolve()的時候,把收到的回覆傳進去。這樣的話,在buyTea裏面咱們就能夠加個判斷,看看是否是收到了朋友的轉帳。如圖:


    一樣的,哪怕是被刪了好友,咱們也能夠看看具體的消息,再確認一遍,讓本身完全絕望,萬念俱灰地讓小助理把好友刪了。其代碼實現和前一種狀況是同樣的,把內容傳到reject()中,這樣在刪除好友前能夠再仔細確認一遍,看看是否是真的被刪除好友了。


    每完成一項任務,小助理要在本身的任務清單記錄本身的工做。無論刪除好友仍是買茶,仍是都沒幹,反正對小助理來講,「給一個朋友發微信消息‘求求你借我30塊買喜茶,謝謝!’」這個任務結束了,它就完成了一項任務。這個時候,調用finally方法就能夠了。


    調用then方法的時候,直接傳入匿名函數體也能夠。

    至於promise實例的catch方法,在必定程度上能夠被調用then方法時傳入的第二個函數替代。catch函數裏的代碼什麼狀況下會被執行?就是Promise的狀態變爲rejected之後,也就是(reject()的狀況);目前爲止,差很少簡單地實現了Promise的基本使用,固然它還有一些更強大的功能,咱們留到下一篇文章再說!

相關文章
相關標籤/搜索