實現(xiàn) Promises/A+ api屬性源碼附帶注解詳情
簡單的 Promises/A+ 實現(xiàn)
博客:webvueblog.github.io/promise/
倉庫地址:promise
MiniPromise.js 實現(xiàn)
MiniPromise.resolve()
MiniPromise.reject()
MiniPromise.then()
MiniPromise.all()
MiniPromise.race()
MiniPromise.any()
MiniPromise.allSettled()
通篇講解promise,學(xué)習(xí)promise相關(guān)內(nèi)容,熟練使用promise;了解promise可以做什么,不可以做什么。
promise是抽象異步處理對象以及對其進行各種操作的組件。promise最初被提出是在E語言中,它是基于并列/并行處理設(shè)計的一編程語言。
我們在 基于JavaScript的異步處理,大都想到使用回調(diào)函數(shù)。
使用回調(diào)函數(shù)的異步處理
getAsync("file.txt", function(error, result) { // 取得失敗時的處理
if (error) { throw error;
} // 取得成功時的處理});復(fù)制代碼node.js 規(guī)定在 JavaScript 的回調(diào)函數(shù)的一個參數(shù)為 error 對象,這是 Node.js 的一般寫法。
promsie 時可以將復(fù)雜的異步處理輕松地進行模式化,寫法:
var promise = getAsyncPromise('file.txt');
promise.then(function(result) { // 獲取文件內(nèi)容成功時的處理}).catch(function(error) { // 獲取文件內(nèi)容失敗時的處理});復(fù)制代碼ES6 Promise 標(biāo)準(zhǔn)
Constructor
Promise類似于 XMLHttpRequest,從構(gòu)造函數(shù) Promise 來創(chuàng)建一個新建的 promise 對象作為接口。
要想創(chuàng)建一個 promise 對象,可以使用 new 來調(diào)用 promise 的構(gòu)造器來進行實例化。
var promise = new Promise(function(resolve, reject) { // 異步處理
// 處理結(jié)束后,調(diào)用 resolve 或 reject});復(fù)制代碼Instance Method
通過 new 生成的 promise 對象為了設(shè)置其值在 resolve (成功)/ reject(失?。?時調(diào)用的回調(diào)函數(shù)可以使用 promise.then() 實例方法。
promise.then(onFulfilled, onRejected)復(fù)制代碼resolve(成功)時
onFulfilled 會被調(diào)用
reject(失敗)時
onRejected 會被調(diào)用
onFulfilled, onRejected 兩個都是可選參數(shù)。
promise.then 成功和失敗時都可以使用。對于異常進行處理時可以采用 promise.then(undefined, onRejected) 這種方式,只指定 reject 時的回調(diào)函數(shù)即可。不過這種情況下請使用 promise.catch(onRejected) 是更好的選擇。
promise.catch(onRejected)復(fù)制代碼Static Method
像 promise 這樣的全局對象還擁有一些靜態(tài)方法。
包括 Promise.all() 還有 Promise.resolve() 等在內(nèi),主要都是一些對 Promise 進行操作的輔助方法。
Promise工作流程
promise工作流程代碼:
function asyncFunction() { return new Promise(function (resolve, reject) { setTimeout(function () {
resolve('Async Hello world');
}, 1000);
});
}復(fù)制代碼new Promise 構(gòu)造器之后,會返回一個 promise 對象
asyncFunction().then(function(value) { console.log(value); // => 'Async Hello world'}).catch(function (error) { console.log(error);
});復(fù)制代碼為promise對象設(shè)置 .then 調(diào)用返回值時的回調(diào)函數(shù)。
asyncFunction 這個函數(shù)會返回 promise 對象,對于這個 promise 對象,我們調(diào)用它的 then 方法來設(shè)置 resolve 后的回調(diào)函數(shù), catch 方法來設(shè)置發(fā)生錯誤時的回調(diào)函數(shù)。
該promise對象會在setTimeout之后的 1000ms 被 resolve, 這時 then 的調(diào)用函數(shù)會被調(diào)用,并輸出 'Async Hello world'。
在這種情況下 catch 的回調(diào)函數(shù)并不會被執(zhí)行(因為promise返回了resolve),不過如果運行環(huán)境沒有提供 setTimeout 函數(shù)的話,那么就會產(chǎn)生異常,在 catch 中設(shè)置的回調(diào)函數(shù)就會被執(zhí)行。
如果不使用catch方法,只使用then方法的話:
只聲明 promise.then(onFulfilled, onRejected)
asyncFunction().then(function (value) { console.log(value);
},function (error) { console.log(error);
});復(fù)制代碼Promise的狀態(tài)
用 new Promise 實例化的 promise 對象有三種狀態(tài):
has-resolution: FulFilled
resolve(成功)時,此時調(diào)用 onFulfilled
has-rejection: Rejected
reject(失敗)時,此時調(diào)用 onRejected
unresolved: Pending
既不是resolve也不是reject的狀態(tài)。也就是promise對象剛被創(chuàng)建后的初始化狀態(tài)等
建議使用:Pending,F(xiàn)ulfilled,Rejected 的狀態(tài)名稱進行講述。
pending -> value -> Fulfilled
pending -> error -> Rejected
狀態(tài)的改變
promise 對象的狀態(tài),從 Pending 轉(zhuǎn)換為 Fulfilled 或 Rejected 之后,這個 promise 對象的狀態(tài)就不會再發(fā)生任何變化。
在 .then 后執(zhí)行的函數(shù)可以肯定地說只會被調(diào)用一次。并且 Fulfilled 和 Rejected 這兩個中的任一狀態(tài)都可以表示為 Settled(不變的)。
Settled
resolve(成功) 或 reject(失敗)
當(dāng) promise 的對象狀態(tài)發(fā)生變化時,用 .then 來定義只會被調(diào)用一次的函數(shù)。
Promise 代碼
創(chuàng)建promise對象的流程:
new Promise(fn) 返回一個 promise 對象
在 fn 中指定異步等處理
處理結(jié)果正常的話,調(diào)用 resolve(處理結(jié)果值)
處理結(jié)果錯誤的話,調(diào)用 reject(Error對象)
用 Promise 來通過異步處理方式來獲取 XMLHttpRequest(XHR)的數(shù)據(jù):
來創(chuàng)建XHR的promise對象
// 運行示例var URL = "http://xxx.org/get";
getURL(URL).then(function onFulfilled(value){console.log(value);
}).catch(function onRejected(error){console.error(error);
});function getURL(URL) { return new Promise(function (resolve, reject) { var req = new XMLHttpRequest();
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();
});
}復(fù)制代碼為promise對象添加處理方法主要有以下兩種
promise對象被 resolve 時的處理(onFulfilled)
promise對象被 reject 時的處理(onRejected)
此時所謂的 通信成功 , 指的就是在被resolve后, promise對象變?yōu)镕ulFilled狀態(tài) 。
被resolve后的處理,可以在 .then 方法中傳入想要調(diào)用的函數(shù)。
用 new Promise 方法創(chuàng)建promise對象
用 .then 或 .catch 添加promise對象的處理函數(shù)
Promise.resolve
靜態(tài)方法 Promise.resolve(value) 可以認(rèn)為是 new Promise() 方法的快捷方式。
Promise.resolve(18); 等同于 可以認(rèn)為是以下代碼的語法糖:
new Promise(function(resolve){
resolve(18);
});復(fù)制代碼方法 Promise.resolve(value); 的返回值也是一個promise對象
Promise.resolve(18).then(function(value){ console.log(value);
})復(fù)制代碼Promise.resolve 方法另一個作用就是將 thenable 對象轉(zhuǎn)換為promise對象。
Promise.resolve 方法另一個作用就是將 thenable 對象轉(zhuǎn)換為promise對象。
ES6 Promises里提到了Thenable這個概念,簡單來說它就是一個非常類似promise的東西。
就像我們有時稱具有 .length 方法的非數(shù)組對象為Array like一樣,thenable指的是一個具有 .then 方法的對象。
這種將thenable對象轉(zhuǎn)換為promise對象的機制要求thenable對象所擁有的 then 方法應(yīng)該和Promise所擁有的 then 方法具有同樣的功能和處理過程,在將thenable對象轉(zhuǎn)換為promise對象的時候,還會巧妙的利用thenable對象原來具有的 then 方法。
到底什么樣的對象能算是thenable的呢,最簡單的例子就是 jQuery.ajax(),它的返回值就是thenable的。
$.ajax('/json/comment.json');// => 擁有 `.then` 方法的對象復(fù)制代碼這個thenable的對象可以使用 Promise.resolve 來轉(zhuǎn)換為一個promise對象。
將thenable對象轉(zhuǎn)換promise對象
var promise = Promise.resolve($.ajax('/json/comment.json'));// => promise對象promise.then(function(value){ console.log(value);
});復(fù)制代碼Promise.resolve 方法可以認(rèn)為它的作用就是將傳遞給它的參數(shù)填充(Fulfilled)到promise對象后并返回這個promise對象。
Promise.reject
Promise.reject(error) 是 new Promise() 方法的快捷方式。
比如 Promise.reject(new Error("出錯了")) 就是下面代碼的語法糖形式。
new Promise(function(resolve,reject){
reject(new Error("出錯了"));
});復(fù)制代碼這段代碼的功能是調(diào)用該promise對象通過then指定的 onRejected 函數(shù),并將錯誤(Error)對象傳遞給這個 onRejected 函數(shù)。
Promise.reject(new Error("BOOM!")).catch(function(error){ console.error(error);
});復(fù)制代碼.then 中指定的方法調(diào)用是異步進行的
var promise = new Promise(function (resolve){ console.log("inner promise"); // 1
resolve(18);
});
promise.then(function(value){ console.log(value); // 3});console.log("outer promise"); // 2復(fù)制代碼function onReady(fn) { var readyState = document.readyState; if (readyState === 'interactive' || readySate === 'complete') {
fn();
} else { window.addEventListener('DOMContentLoaded', fn);
}
}
onReady(function () { console.log('DOM fully loaded and parsed');
});復(fù)制代碼then
promise可以寫成方法鏈的形式
aPromise.then(function taskA(value){ // task A}).then(function taskB(vaue){ // task B}).catch(function onRejected(error){ console.log(error);
});復(fù)制代碼promise chain
promise-then-catch-flow.js
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);復(fù)制代碼

then
注冊 onFulfilled 時的回調(diào)函數(shù)
catch
注冊 onRejected 時的回調(diào)函數(shù)
promise chain 中如何傳遞參數(shù)
return 返回值
每個方法中 return 的值不僅只局限于字符串或者數(shù)值類型,也可以是對象或者promise對象等復(fù)雜類型
return的值會由 Promise.resolve(return的返回值); 進行相應(yīng)的包裝處理,因此不管回調(diào)函數(shù)中會返回一個什么樣的值,最終 then 的結(jié)果都是返回一個新創(chuàng)建的promise對象
Promise.then 不僅僅是注冊一個回調(diào)函數(shù)那么簡單,它還會將回調(diào)函數(shù)的返回值進行轉(zhuǎn)換,創(chuàng)建并返回一個 promise 對象。
catch
Promise.cath 只是 promise.then(undefined, onRejected); 方法的一個別名而已。這個方法用來注冊當(dāng)promise對象狀態(tài)變?yōu)镽ejected時的回調(diào)函數(shù)。
var promise = Promise.reject(new Error("message"));
promise.catch(function (error) { console.error(error);
});復(fù)制代碼解決Promise.catch標(biāo)識符沖突問題
var promise = Promise.reject(new Error("message"));
promise["catch"](function (error) { console.error(error);
});var promise = Promise.reject(new Error("message"));
promise.then(undefined, function (error) { console.error(error);
});復(fù)制代碼每次調(diào)用then都會返回一個新創(chuàng)建的promise對象
不管是 then 還是 catch 方法調(diào)用,都返回了一個新的promise對象。
var aPromise = new Promise(function (resolve) {
resolve(100);
});var thenPromise = aPromise.then(function (value) { console.log(value);
});var catchPromise = thenPromise.catch(function (error) { console.error(error);
});console.log(aPromise !== thenPromise); // => trueconsole.log(thenPromise !== catchPromise);// => true復(fù)制代碼then 和 catch 都返回了和調(diào)用者不同的promise對象。
promise object -> then(promise object)(value -> onFulfilled; error -> onRejected) -> catch(error -> onRejected)
then 的使用方式上的差別


Promise.all
Promise.all 接收一個 promise對象的數(shù)組作為參數(shù),當(dāng)這個數(shù)組里的所有promise對象全部變?yōu)閞esolve或reject狀態(tài)的時候,它才會去調(diào)用 .then 方法。
使用一個計時器來計算一下程序執(zhí)行時間
// `delay`毫秒后執(zhí)行resolvefunction timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () {
resolve(delay);
}, delay);
});
}var startDate = Date.now();// 所有promise變?yōu)閞esolve后程序退出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]});復(fù)制代碼Promise.race
Promise.race 方法 ,接收一個promise對象數(shù)組為參數(shù)。
Promise.all 在接收到的所有的對象promise都變?yōu)?FulFilled 或者 Rejected 狀態(tài)之后才會繼續(xù)進行后面的處理, 與之相對的是 Promise.race 只要有一個promise對象進入FulFilled 或者 Rejected 狀態(tài)的話,就會繼續(xù)進行后面的處理。
then 與 catch 比較
使用 promise.then(onFulfilled, onRejected) 的話
在 onFulfilled 中發(fā)生異常的話,在 onRejected 中是捕獲不到這個異常的。
在 promise.then(onFulfilled).catch(onRejected) 的情況下
then 中產(chǎn)生的異常能在 .catch 中捕獲
.then 和 .catch 在本質(zhì)上是沒有區(qū)別的
需要分場合使用。
由于 .catch 方法是 .then 的別名,我們使用 .then 也能完成同樣的工作。只不過使用 .catch 的話意圖更明確,更容易理解
練習(xí)
then
promise.then(onFulfilled, onRejected);

catch
promise.catch(onRejected);

resolve
Promise.resolve(promise);
Promise.resolve(thenable);
Promise.resolve(object);

接收到promise對象參數(shù)的時候
返回的還是接收到的promise對象
接收到thenable類型的對象的時候
返回一個新的promise對象,這個對象具有一個 then 方法
接收的參數(shù)為其他類型的時候(包括JavaScript對或null等)
返回一個將該對象作為值的新promise對象
reject
Promise.reject(object)
和 Promise.resolve不同的是,即使Promise.reject接收到的參數(shù)是一個promise對象,該函數(shù)也還是會返回一個全新的promise對象。
all
Promise.all(promiseArray);
生成并返回一個新的promise對象。
參數(shù)傳遞promise數(shù)組中所有的promise對象都變?yōu)閞esolve的時候,該方法才會返回, 新創(chuàng)建的promise則會使用這些promise的值。
如果參數(shù)中的任何一個promise為reject的話,則整個Promise.all調(diào)用會立即終止,并返回一個reject的新的promise對象。
由于參數(shù)數(shù)組中的每個元素都是由 Promise.resolve 包裝(wrap)的,所以Paomise.all可以處理不同類型的promose對象。
race
Promise.race(promiseArray)
生成并返回一個新的promise對象。
參數(shù) promise 數(shù)組中的任何一個promise對象如果變?yōu)閞esolve或者reject的話, 該函數(shù)就會返回,并使用這個promise對象的值進行resolve或者reject。
