Promise學習總結

寫在前面:
第一遍學Promise時, 只是大概過了一遍, 感受學的不夠深刻, 這一篇算是對以前的一個總結吧. Promise在ES6中也屬於一個較難理解的一部分; 因此在學習一個比較難理解的知識點時, 咱們能夠圍繞這個知識點進行展開,逐個去理解.前端

再探Promise

理解一個知識點, 不妨先列出下面幾個問題.ajax

  • Promise是用來幹什麼的?
  • Promise是什麼?
  • Promise如何去建立,使用?
  • Promise的經常使用形式?
  • Promise的使用有哪些注意點?

異步相關背景介紹

瀏覽器內核

首先聊一下瀏覽器, 一直對瀏覽器的結構比較好奇,查了不少資料總結就有下面一點相關總結; 其中也借鑑其餘人的一些東西.
瀏覽器是多進程的,有主進程, GPU加速進程,渲染進程(內核)等, 通常新開一個tab頁面就是新啓動一個進程, CPU就會給他分配資源; 但其中有一個核心進程==>渲染進程(瀏覽器內核), 是咱們前端人員須要特別關注的,它包括了多個線程...編程

  • GUI渲染線程promise

    負責渲染瀏覽器界面,解析HTML,CSS,構建DOM樹和RenderObject樹,佈局和繪製等。
    當界面須要重繪(Repaint)或因爲某種操做引起迴流(reflow)時,該線程就會執行
    
    注意,GUI渲染線程與JS引擎線程是互斥的,當JS引擎執行時GUI線程會被掛起(至關於被凍結了),
    GUI更新會被保存在一個隊列中等到JS引擎空閒時當即被執行。
  • JS引擎線程瀏覽器

    也稱爲JS內核,負責處理Javascript腳本程序.(例如V8引擎)JS引擎線程負責解析Javascript腳本,運行代碼。
    JS引擎一直等待着任務隊列中任務的到來,而後加以處理,一個Tab頁(renderer進程)中不管何時都只有一個JS線程在運行JS程序
    一樣注意,GUI渲染線程與JS引擎線程是互斥的,因此若是JS執行的時間過長,這樣就會形成頁面的渲染不連貫,致使頁面渲染加載阻塞。
    
    不過H5中新增了Web Worker, 實現了多線程: js會新開線程來處理一些其餘任務,但不會影響DOM結構...
    建立Worker時,JS引擎向瀏覽器申請開一個子線程(子線程是瀏覽器開的,徹底受主線程控制,並且不能操做DOM)
    JS引擎線程與worker線程間經過特定的方式通訊
    (postMessage API,須要經過序列化對象來與線程交互特定的數據)
  • 事件觸發線程網絡

    這個線程是歸屬於瀏覽器而不是JS引擎,用來控制事件循環(能夠理解,JS引擎本身都忙不過來,須要瀏覽器另開線程協助)
    當JS引擎執行代碼塊如setTimeOut時(也可來自瀏覽器內核的其餘線程,如鼠標點擊、AJAX異步請求,
    頁面滾動等),會將對應任務添加到事件線程中
    當對應的事件符合觸發條件被觸發時,該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理
    注意,
    因爲JS的單線程關係,因此這些待處理隊列中的事件都得排隊等待JS引擎處理(當JS引擎空閒時纔會去執行)
  • 定時觸發器線程多線程

    即setInterval與setTimeout所在線程
    瀏覽器定時計數器並非由JavaScript引擎計數的,(由於JavaScript引擎是單線程的, 若是處於阻塞線程狀態就會影響記計時的準確); 
    所以經過單獨線程來計時並觸發定時(計時完畢後,添加到事件隊列中,等待JS引擎空閒後執行)
    固然setTimeout中的延時參數也不必定準確
  • 異步HTTP請求線程cors

    在XMLHttpRequest在鏈接後是經過瀏覽器新開一個網絡線程去請求
    將檢測到狀態變動時,若是設置有回調函數,異步線程就產生狀態變動事件,
    將這個回調再放入事件隊列中。再由JavaScript引擎執行。

那麼關於瀏覽器方面的背景知識就介紹到這裏啦, 想要深刻去了解,能夠去查相關資料...異步

事件隊列和循環

你們都知道JavaScript引擎是單線程的工做模式, 即同一時間只能跑一段代碼,還要按順序自上而下執行; 可是碰到I/O操做, 定時器, 事件監聽函數等這些耗時操做; JS引擎不會等待它們有結果了纔去之下它們後面的代碼, 而是會將它們扔進任務(事件)隊列中, 等待同步代碼執行棧空了以後, 再去任務隊列將任務一個個取出來執行任務所對應的回調函數, 執行完畢後會一直等待新的任務到來; 如此循環...異步編程

幾個類型的回調

  • 同步回調函數

    咱們能夠利用了函數的執行棧順序,函數做爲參數放到另外一個函數中調用, 誰在後面調用誰就先被放在函數執行棧棧頂

  • 異步回調函數

    事先在外面定義好一個callback; 將回調函數做爲某個函數的參數, 利用函數的做用域將函數中異步任務獲得的結果存在回調函數的形參中, 而後在函數體末尾調用...

  • 定時器
    setTimeout的做用是在間隔必定的時間後,將回調函數插入任務隊列中,等棧中的同步任務都執行完畢後,再執行, 固然這個時間不必定準確...

Promise是用來幹什麼的?

看阮老師的ES6出門上說Promise是JS異步編程的一種解決方案. 舉個例子, Ajax的回調問題, 若是下一個ajax請求要用到上一個Ajax請求中的結果, 那麼每每就會致使多個回調嵌套的問題, 那麼Promise就能夠解決這種代碼上的嵌套問題, 是咱們的代碼變得更優美, 更利於維護; 我暫時先對Promise的理解就是: 處理異步任務, 保存異步結果狀態, 異步代碼同步化...

Promise是什麼?

Promise 它就是一個對象,至關於一個容器, 裏面存的就是一個異步操做的結果; 咱們能夠是從中獲取異步操做結果的相關信息。

Promise對象表明一個未完成、但預計未來會完成的操做。
它有如下三種狀態:

pending:初始值,不是fulfilled,也不是rejected
fulfilled:表明操做成功
rejected:表明操做失敗

Promise有兩種狀態改變的方式,既能夠從pending轉變爲fulfilled,也能夠從pending轉變爲rejected。一旦狀態改變,就「凝固」了,會一直保持這個狀態,不會再發生變化。當狀態發生變化,promise.then綁定的函數就會被調用。
注意:Promise一旦新建就會「當即執行」(它屬於microtask),沒法取消。這也是它的缺點之一。

Promise的建立和使用?

1.建立promise對象

//1.使用new Promise(func)的形式
//2.快捷語法: Promise.resolve(func) || Promise.reject(func)
// 參數1: 通常是一個處理異步任務的函數  
// 返回值: 一個promise實例對象
Promise.resolve('foo')
// 等價於, 不過參數類型不同執行的操做也會有所不一樣
new Promise(resolve => resolve('foo'))

2.在函數func中 放異步處理代碼

// 傳入兩個參數: 回調函數resolve, reject分別去保存異步處理的結果
// 成功: 使用resolve(結果)
// 失敗: 使用reject(緣由)

3.調用實例的then(func1) 或者 catch(err)

首先then方法是異步執行, 對上面的異步結果進行處理的函數
參數: 傳回調函數, 一個兩個都行, 前者是成功狀態的回調,後者是失敗的回調

Promise經常使用的場景?

  • promise通常的使用套路就是:

    1.先定義一個函數, 函數內部使用new Promise()的方式來返回一個promise對象, resolve用來保存 異步處理成功的結果
    reject用來保存 異常處理的結果
    2.而後函數調用,傳參
    3.鏈式語法點出then方法, then中的回調用來處理異步結果
    4.有錯誤就點出catch方法, 也能夠用then(null, function() {})代替catch
    5.then的回調中也可return一個值, 會被包裝成一個新的promise, 所以能夠繼續調用then方法
  • 應用場景: 在ajax中使用, 解決異步嵌套問題

function ajax(url) {

        return new Promise((resolve, reject) => {
    
            let xhr = new XMLHttpRequest();
            // 請求類型, 地址, 異步
            xhr.open('get', url, true);
            xhr.send();
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    try {
                        // 處理響應內容, 將內容丟到成功狀態的回調
                        resolve(JSON.parse(xhr.responseText))
                    } catch (e) {
                        // 捕獲錯誤, 丟到失敗狀態的回調
                        reject(e)
                    }
                }
            }
        });
    }

    // 調用 封裝的ajax函數
    let url = 'http://127.0.0.1:3000/xxoo'; // 本身本地開的一個服務

    ajax(url)
        .then(res => console.log(res)) // 輸出 {code: 0, msg: 'hello cors'}
        .catch(err => console.log(err))

```
  • 其餘場景

    // 實現串行任務管道; 即當前任務的輸出能夠做爲下一個任務的輸入,造成一條數據管道;
        // 好比: 好比從url1獲取參數userId,拿到後再從url2獲取第三方openId,最後再從url3貨取orderList,
        而後把結果展現給用戶,相似的邏輯都是任務管道:
        
        new Promise(function(resolve, reject) {
            resolve(1);
        })
        .then(function(res) {
            return new Promise(function(resolve, reject) {
                resolve(res + 1);
            });
        })
        .then(function(res) {
            return new Promise(function(resolve, reject) {
                resolve(res + 1);
            });
        })
            .then(function(res) {
            console.log(res); // 3
        });

promise的好處

  • 在異步執行的流程中,使用Promise能夠把 執行代碼 和 處理結果 的代碼清晰地分離
    這樣咱們即可以 把執行代碼 和 結果處理 分紅不一樣的模塊來寫,易於維護

  • 減小異步回調的嵌套, 好比ajax回調, 咱們能夠依次調用then方法便可, 還能夠控制回調的順序
  • 多個異步任務是爲了容錯去訪問用同一資源時, 可使用Promise.race([promise實例...])
  • 多個異步任務並行執行時,好比ajax訪問兩個接口, 能夠用Promise.all([promise實例...])

Promise使用的注意事項

  1. Promise構造函數內的同步代碼當即執行
  2. 回調函數參數resolve異步執行, 將結果做爲參數傳給then方法中的回調函數
  3. resolve只有第一次執行有效,狀態不能二次改變
  4. then和catch若是有return, 返回的是一個全新的promise對象, 能夠鏈式調用
  5. Promise構造函數只會執行一次, promise實例會保存resolve的狀態,
    之後這個實例每次調用then都是返回一個這個狀態, 若鏈式調用then,下一個則會打印undefined, res沒有值...
  6. then中返回任意一個非 promise 的值都會被包裹成 promise 對象
  7. .then 或 .catch 返回的值不能是 promise 自己,不然會形成死循環
  8. .then 或者 .catch 的參數指望是函數,傳入非函數則會發生值穿透。
  9. .then 能夠接收兩個參數,第一個是處理成功的函數,第二個是處理錯誤的函數。.catch 是 .then 第二個參數的簡便寫法,可是它們用法上有一點須要注意:.then 的第二個處理錯誤的函數捕獲不了第一個處理成功的函數拋出的錯誤,然後續的 .catch 能夠捕獲以前的錯誤。
相關文章
相關標籤/搜索