文章出處

  Promise的基本使用:

  利用Promise是解決JS異步執行時候回調函數嵌套回調函數的問題, 更簡潔地控制函數執行流程;

  通過new實例化Promise,  構造函數需要兩個參數, 第一個參數為函數執行成功以后執行的函數resolve, 第二個函數為函數執行失敗以后執行的函數reject:

new Promise(function(resolve , reject) {
});

  通過Promise,我們把回調函數用線性的方式寫出來,而不是一層套一層, 這個函數有四層回調;

fn("args", function(a) {
    fn1("foo", function(b) {
        fn2("bar", function(c) {
            fn3("baz", function(d) {
                alert("回調成功,獲知的內容為:"+a+b+c+d)
            })
        })
    })
})

  以上的Demo只有包含成功的回調, 如果失敗的回調也算的話, 也就更麻煩了;

  如果使用Promise的方式,我們可以改裝成線性的代碼, 更加符合閱讀的習慣,只要在then函數下寫邏輯即可;

new Promise(function(resolve , reject) {
    resolve(1);
}).then(function(val) {
    console.log(val);
    return new Promise(function(resolve , reject) {
        resolve(2);
    });
}).then(function(val) {
    console.log(val);
    return new Promise(function(resolve , reject) {
        resolve(3);
    });
}).then(function(val) {
    console.log(val);
    return new Promise(function(resolve , reject) {
        resolve(4);
    });
}).then(function(val) {
    console.log(val);
});

  這是一個ajax異步獲取數據的例子, 我們使用了回調函數

<html>
<head>
    <meta charset="utf-8">
</head>
<body>
<script>
    var callback = function(res) {
        console.log(res);
    };
    var ajax = function(url, callback) {
        var r = new XMLHttpRequest();
        r.open("GET", url, true);
        r.onreadystatechange = function () {
            if (r.readyState != 4 || r.status != 200) return;
            var data = JSON.parse(r.responseText);
            callback(data);
        };
        r.send();
    };
    //執行請求:
    ajax("http://www.filltext.com?rows=10&f={firstName}", callback);
    //再做別的事情;
</script>
</body>
</html>

  因為ES6內置了Promise, 我們可以把以上的callback改寫成promise的方式, 首先ajax函數返回一個Promise對象;

<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <script>
        var callback = function(res) {
            console.log(res);
        };
        var ajax = function(url) {
            return new Promise(function(resolve, reject) {
                var r = new XMLHttpRequest();
                r.open("GET", url, true);
                r.onreadystatechange = function () {
                    if (r.readyState != 4 || r.status != 200) return;
                    var data = JSON.parse(r.responseText);
                    resolve(data);
                };
                r.send();
            })
        };
        //執行請求:
        ajax("http://www.filltext.com?rows=10&f={firstName}").then(function(data) {
            callback(data);
        });
        //再做別的事情;
    </script>
</body>
</html>

  Promise實例的三種狀態:

  每一個實例化的Promise都有三個狀態;pending(等待)  rejected(拒絕)  resolved(解決) ,默認的狀態為pending,如果執行了resolve(), 那么這個promise的狀態會變為resolve,如果執行了reject(), 那么這個promise的狀態就會變成rejected, 而且這些狀態是不可撤銷的,一經更改,不會再變了;

  then方法:

  promise有一個then方法,then方法接收兩個參數, 第一個為函數的成功回調, 第二個為函數的失敗回調:

var promise = new Promise(function(resolve , reject) {
    resolve(); //執行成功回調;
});
console.log(0);
promise.then(function(val) {
    console.log(1); 
}, function() {
    console.log("失敗");
});
console.log("2");
var promise = new Promise(function(resolve , reject) {
    reject();
});
console.log(0);
promise.then(function(val) {
    console.log(1);
}, function() {
    console.log("失敗");
});
console.log("2");

  then方法每一次都是返回不同的Promise實例,then的第一個參數是成功回調, 這個成功回調的參數為: 上一個Promise實例執行resolve方法的參數;

  一般來說, then方法會返回當前的promise, 如果在then方法里面return 一個新的Promise實例,那么此時的then返回的就是新的Promise實例, 利用這個特性,就可以實現多層回調

new Promise(function(resolve , reject) {
    resolve(1);
}).then(function(val) {
    console.log(val);
    return new Promise(function(resolve , reject) {
        resolve(2);
    });
}).then(function(val) {
    console.log(val);
    return new Promise(function(resolve , reject) {
        resolve(3);
    });
}).then(function(val) {
    console.log(val);
    return new Promise(function(resolve , reject) {
        resolve(4);
    });
}).then(function(val) {
    console.log(val);
});

  不管代碼是異步還是同步的, 都可以用Promise的then方法, 同步的代碼直接寫在then方法第一個參數, 把需要參數通過resolve傳給下一個then方法,

  如果是異步的代碼, 就直接return一個Promise實例:

new Promise(function(resolve , reject) {
    resolve(1);
}).then(function(val) {
    console.log(val);
    return 2;
}).then(function(val) {
    console.log(val);
    return 3;
}).then(function(val) {
    console.log(val);
    return new Promise(function(resolve,reject) {
        //異步操作些這里
        resolve(4);
    });
}).then(function(val) {
    console.log(val);
    return 5;
}).then(function(val) {
    console.log(val);
});

  catch方法:

  catch方法和失敗回調時一樣的, 如果上一個異步函數拋出了錯誤了, 錯誤會被捕獲, 并執行catch方法或者失敗回調;

var promise = new Promise(function(resolve , reject) {
    resolve(); //執行成功回調;
});
console.log(0);
promise.then(function(val) {
    console.log("成功");
    throw new Error("heheda");
}).catch(function(e) {
    console.log(e);
}).then(function() {
    console.log("繼續執行");
});

 

  Promise中的錯誤是會一層層傳遞的, 如果錯誤沒有沒有被捕獲, 會一直傳遞給下一個promise對象, 直到被捕獲為止, 然后繼續往下執行:

new Promise(function(resolve , reject) {
    resolve(1);
}).then(function(val) {
        console.log(val);
        return new Promise(function(resolve , reject) {
            throw new Error("err");
        });
    }).then(function(val) {
        console.log(val);
        return new Promise(function(resolve , reject) {
            resolve(3);
        });
    }).then(function(val) {
        console.log(val);
        return new Promise(function(resolve , reject) {
            resolve(4);
        });
    }).then(function(val) {
        console.log(val);
    }).catch(function(err) {
        console.log(err);
    }).then(function() {
        console.log("繼續執行")
    })

  構造函數Promise的四個方法:

  構造函數Promise有四個方法, Promise.all, Promise.race, Promise.reject, Promise.resolve:

  Promise.all(iterable)
    返回一個promise對象,當iterable參數里所有的promise都被解決后,該promise也會被解決

    要注意all方法是Promise函數的方法,不是實例的方法, 參數是一個數組, 數組里面全是Promise的實例 : 

var p0 = new Promise(function(resolve) {
    setTimeout(function() {
        resolve(0)
    },1000);
})
var p1 = new Promise(function(resolve) {
    setTimeout(function() {
        resolve(1)
    },2000);
})
var p2 = new Promise(function(resolve) {
    setTimeout(function() {
        resolve(2)
    },3000);
})
Promise.all([p0,p1,p2]).then(function(arr) {
    console.log(arr)
})

  Promise.race(iterable)

    當iterable參數里的任意一個子promise被成功或失敗后,父promise馬上也會用子promise的成功返回值或失敗詳情作為參數調用父promise綁定的相應句柄,并返回該promise對象。
  Promise.reject(reason)
    調用Promise的rejected句柄,并返回這個Promise對象。
  Promise.resolve(value)

    用成功值value解決一個Promise對象。如果該value為可繼續的(thenable,即帶有then方法),返回的Promise對象會“跟隨”這個value,采用這個value的最終狀態;否則的話返回值會用這個value滿足(fullfil)返回的Promise對象。

  官方的例子:

<html>
<head>
    <meta charset="utf-8">
</head>
<body>
<div id="log"></div>
<script>
    'use strict';
    var promiseCount = 0;
    function testPromise() {
        var thisPromiseCount = ++promiseCount;

        var log = document.getElementById('log');
        log.insertAdjacentHTML('beforeend', thisPromiseCount + ') 開始(同步代碼開始)<br/>');

        // 我們創建一個新的promise: 然后用'result'字符串解決這個promise (3秒后)
        var p1 = new Promise(function (resolve, reject) {
            // 解決函數帶著解決(resolve)或拒絕(reject)promise的能力被執行
            log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise開始(異步代碼開始)<br/>');

            // 這只是個創建異步解決的示例
            window.setTimeout(function () {
                // 我們滿足(fullfil)了這個promise!
                resolve(thisPromiseCount)
            }, Math.random() * 2000 + 1000);
        });

        // 定義當promise被滿足時應做什么
        p1.then(function (val) {
            // 輸出一段信息和一個值
            log.insertAdjacentHTML('beforeend', val + ') Promise被滿足了(異步代碼結束)<br/>');
        });

        log.insertAdjacentHTML('beforeend', thisPromiseCount + ') 建立了Promise(同步代碼結束)<br/><br/>');
    }
    testPromise();
</script>
</body>
</html>

  既然有了Promise , 我們就可以把封裝XMLHttpRequest封裝成GET方法, 方便使用:

function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}

  然后使用:

get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
});

  假數據的地址可以自己設置, 可以通過控制臺請求, 注意跨域的問題;

  封裝XMLHttpRequest成Promise異步加載圖片的案例:https://github.com/mdn/promises-test/blob/gh-pages/index.html

  其他:

  以上只是Promise的一些基礎知識, 還有一些其他的知識點, 因為能力有限不一一介紹了(Promise.resolve的不同參數, 與Generator一起使用, Promise的附加方法, 等等等等);

  把Promise的運行流程畫出來, 對Promise的理解會好一點, Promise還是比較繞的

  瀏覽器支持情況:

  Chrome 32, Opera 1,Firefox 29, Safari 8 ,Microsoft Edge, 這些瀏覽器以上都默認支持; 

   參考

  Promises/A+ 規范: https://promisesaplus.com/

  ES6Promises的polyfill : https://github.com/stefanpenner/es6-promise#readme

  html5rocks:http://www.html5rocks.com/en/tutorials/es6/promises/

  阮老師:http://es6.ruanyifeng.com/#docs/promise

作者: NONO
出處:http://www.cnblogs.com/diligenceday/
QQ:287101329
微信:18101055830 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()