在前端開發中,有個很熟悉的詞叫作「回調」,在處理一些異步的函數的時候,回調被普遍應用,可是大量用回調來編程,會出現嵌套層級過多,代碼風格不規範,不清晰的問題。「Promise/A+規範」是一種很方便的異步編程方式。前端
- 將複雜的異步處理輕鬆地進行模式化
- 代碼更清晰
- 異常處理更方便
- 代碼鏈式操做,爽!
先來一段簡單的promise的代碼:ajax
var promise = getAsyncPromise("fileA.txt");
promise.then(function(result){
// 獲取文件內容成功時的處理
}).catch(function(error){
// 獲取文件內容失敗時的處理
});
複製代碼
經過這個代碼,是否是以爲promise異步編程很清晰?編程
下面我詳細介紹下promise實用的方法。json
var promise = new Promise(function(resolve, reject) {
// 異步處理
// 處理結束後、調用resolve 或 reject
});
複製代碼
promise.then(onFulfilled, onRejected)
//resolve(成功)時 onFulfilled 會被調用
//reject(失敗)時onRejected 會被調用
//onFulfilled 、 onRejected 兩個都爲可選參數。
複製代碼
promise.catch(onRejected)
複製代碼
function asyncFunction() {
return new Promise(function (resolve, reject) { //①
setTimeout(function () {
resolve('Async Hello world'); }, 16); //②
});
}
asyncFunction().then(function (value) { console.log(value); // => 'Async Hello world'
}).catch(function (error) { console.log(error);
});
複製代碼
栗子說明:數組
上面的代碼也也能夠不用catch方法,用then(resolve, reject)的形式(鏈式操做時最好用catch,能夠統一捕捉異常):promise
asyncFunction().then(function (value) { console.log(value);
}, function (error) { console.log(error);
});
複製代碼
大體的瞭解promise處理流程,單獨介紹一下promise狀態bash
*注意:從Pending轉換爲Fulfilled或Rejected以後, 這個promise對象的狀 態就不會再發生任何變化。異步
當promise的對象狀態發生變化時,用 .then 來定義只會被調用一次的函數。async
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest(); //栗子就不作ie兼容了
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
}
else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
// 運行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value) {
console.log(value); }).catch(function onRejected(error) {
console.error(error); });
複製代碼
栗子說明:異步編程
讓咱們在實際中使用一下剛纔建立的返回promise對象的函數
getURL("http://example.com/"); // => 返回promise對象
複製代碼
爲promise對象添加處理方法主要有如下兩種:
getURL(URL).then(onFulfilled, onRejected); //純then寫法
getURL(URL).then( onFulfilled).catch( onRejected); // then chatch寫法
複製代碼
注:通常說來,使用 .catch 來將resolve和reject處理分開來寫是比較推薦的作法, 這二者的 區別會在then和catch的區別中再作詳細介紹。
//好比 Promise.resolve(42); 能夠認爲是如下代碼的語法糖。
new Promise(function(resolve){
resolve(42);
});
複製代碼
在這段代碼中的 resolve(42);會讓這個promise對象當即進入肯定(即resolved)狀態,並將 42 傳遞給後面then裏所指定的 onFulfilled 函數。
Promise.resolve(42).then(
function (value) {
console.log(value) //42
}
);
複製代碼
注:Promise.resolve做爲newPromise()的快捷方式,在進行promise對象的初始化或者編寫 測試代碼的時候都很是方便。
Promise.resolve方法另外一個做用就是將thenable對象轉換爲promise對象。
**thenable對象**:簡單來講它就是一個很是相似promise的東西(*就像咱們有時稱具備.length方法的非數組對象爲Arraylike同樣,thenable指的是一個具備.then 方法的對象*。)
複製代碼
最簡單的例子就是jQuery.ajax(),它的返回值就是thenable的(由於 jQuery.ajax()的返回值是jqXHRObject對象,這個對象具備 .then 方法)
用Promise.resolve來轉換爲一個promise對象。變成了promse對象的話,就能直接使用 then 或者 catch等這些在ES6Promises裏定 義的方法了。
var promise = Promise.resolve(
$.ajax('/json/comment.json')
) // => promise對象
promise.then(function(value){
console.log(value);
});
複製代碼
總結:簡單總結一下 Promise.resolve 方法的話,能夠認爲它的做用就是將傳遞給它的參數填 充(Fulfilled)到promise對象後並返回這個promise對象。
new Promise(
function (resolve,reject) {
reject(new Error("出錯了"));
}
);
// 是Promise.reject(newError("出錯了"))的語法糖。
//以下:
Promise.reject(new Error("BOOM!")).catch(
function(error){
console.error(error);
}
);
複製代碼
aPromise.then(function taskA(value) {
// task A
}).then(function taskB(vaue){
// task B
}).catch(function onRejected(error){ //最後catch 統一捕捉異常
console.log(error);
});
複製代碼
來段代碼,看下鏈式操做的執行流程
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
複製代碼
上面這段代碼的執行流程以下圖:
註釋: .cntch() 能夠捕捉 taskA taskB出現的異常,可是 onRejected、finalTask這兩個後面沒有catch捕捉了,因此這兩個函數出現問題不會被捕捉到。
前面例子中的Task都是相互獨立的,只是被簡單調用而已。這時候若是 Task A 想給 Task B 傳遞一個參數,那就是在 Task A 中 return 的返回值,會在 Task B 執行時傳給它。
function doubleUp(value) {
return value * 2;
}
function increment(value) {
return value + 1;
}
function output(value) {
console.log(value);
}
var promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){
// promise chain中出現異常的時候會被調用
console.error(error);
});
1. Promise.resolve(1); 傳遞 1 給 increment 函數
2. 函數 increment 對接收的參數進行 +1 操做並返回(經過 return ) 3. 這時參數變爲2,並再次傳給 doubleUp 函數
3. 最後在函數 output 中打印結果
複製代碼
**注:**每一個方法中return的值不只只侷限於字符串或者數值類型,也能夠是對象或者promise 對象等複雜類型。
Promise.all 接收一個promise對象的數組做爲參數,當這個數組裏的全部promise對象 所有變爲resolve或reject狀態的時候,它纔會去調用 .then 方法。
// `delay`毫秒後執行resolve
function timerPromisefy(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay); }, delay
);
});
}
var startDate = Date.now();
// 全部promise變爲resolve後程序退出
Promise.all([
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
]).then(function (values) {
console.log(Date.now() - startDate + 'ms'); // 約128ms
console.log(values); // [1,32,64,128]
}
);
複製代碼
**注:**從總用時120ms來看,傳遞給Promise.all的promise並非一個個的順序執行的,而是 同時開始、並行執行的。
它的使用方法和Promise.all同樣,接收一個promise對象數組爲參數
Promise.all 在接收到的全部的對象promise都變爲 FulFilled 或者 Rejected 狀態以後纔會繼續進行後面的處理,與之相對的是Promise.race 只要有一個promise對象進入FulFilled或者Rejected狀態的話,就會繼續進行後面的處理。
Promise.race([
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
]).then(function (value) {
console.log(value); // => 1 只打印出一個最早出結果的
});
複製代碼