Promise是一種異步編程解決方案肺孵,可以使異步代碼更加優(yōu)雅吓肋。
例如,我們需要進行這么一個操作:
- 向一個url獲取一個名字
- 根據(jù)這個名字獲取一個數(shù)據(jù)
- 根據(jù)這個數(shù)據(jù)獲取到我們需要結(jié)果
使用回調(diào)函數(shù):
get(url, function(name){
get(name, function(data){
get(data, function(result){
console.log(result);
}, errHanler)
}, errHanler)
}, errHanler)
function errHanler(err){
console.error(err);
}
使用Promise:
get(url).then((name)=>{
return get(name);
}).then((data)=>{
return get(data);
}).then((result)=>{
console.log(result);
}).catch((err)=>{
console.error(err);
})
使用promise可以避免層層嵌套的情況初婆。除此之外蓬坡,ES6中的Promise還有all、race等方便的操作磅叛。(ES6 Promise詳細介紹)
ES6的Promise是Promise A+規(guī)范的一種實現(xiàn)屑咳。(Promise A+ 規(guī)范翻譯)
現(xiàn)在試著自己實現(xiàn)一個Promise。
首先一個promise擁有屬性狀態(tài)弊琴,初始時為Pennding兆龙,且可遷移到Fulfilled或Rejected。
一個promise必須擁有then方法敲董,并可通過then訪問它的值/拒因紫皇。
class P {
constructor() {
this[Symbol.for("PromiseStatus")] = "pennding";
this[Symbol.for("PromiseValue")] = undefined;
}
then(onFulfilled, onRejected) {
const status = this[Symbol.for("PromiseStatus")];
if (status == "pennding") {
this.onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected == 'function' ? onRejected : null;
} else if (status == "fulfilled") {
onFulfilled(this[Symbol.for("PromiseValue")]);
} else if (status == "rejected") {
onFulfilled(this[Symbol.for("PromiseValue")]);
}
}
}
promise有resolve、reject方法腋寨,將promise的狀態(tài)分別遷移為Fulfilled或Rejected聪铺。promise的狀態(tài)只能改變一次。
然后promise構(gòu)造函數(shù)接收一個函數(shù)作為參數(shù)萄窜,并往該函數(shù)傳入resolve铃剔、reject。
class P {
constructor() {
this[Symbol.for("PromiseStatus")] = "pennding";
this[Symbol.for("PromiseValue")] = undefined;
if (typeof fn !== "function") {
throw new TypeError(`Promise resolver ${typeof fn} is not a function`);
}
fn(this.resolve.bind(this), this.reject.bind(this));
}
resolve(data) {
if (this[Symbol.for("PromiseStatus")] == "pennding") {
this[Symbol.for("PromiseStatus")] = "fulfilled";
this[Symbol.for("PromiseValue")] = data;
this.onFulfilled(data);
}
}
reject(reason) {
if (this[Symbol.for("PromiseStatus")] == "pennding") {
this[Symbol.for("PromiseStatus")] = "rejected";
this[Symbol.for("PromiseValue")] = reason;
this.onRejected(reason);
}
}
}
為了保證onFulfilled和onRejected異步執(zhí)行查刻,resove或reject被調(diào)用時不能馬上調(diào)用键兜,需要在當前一輪事件循環(huán)結(jié)束后再調(diào)用onFulfilled/onRejected∷氡茫可通過setTimeout來實現(xiàn)
resolve(data) {
if (this[Symbol.for("PromiseStatus")] == "pennding") {
this[Symbol.for("PromiseStatus")] = "fulfilled";
this[Symbol.for("PromiseValue")] = data;
setTimeout(() => {
this.onFulfilled(data);
});
}
}
then方法可以被連續(xù)調(diào)用普气,所以需要增加onFulfilledList、onRejectedList兩個數(shù)組佃延,再resolve/reject被調(diào)用時遍歷數(shù)組執(zhí)行现诀。
最后,then方法會返回一個新的promise履肃,并將onFulfilled/onRejected中的返回值傳遞給新promise赶盔。
最終版:
class P {
constructor(fn) {
this[Symbol.for("PromiseStatus")] = "pennding";
this[Symbol.for("PromiseValue")] = undefined;
this.onFulfilledList = [];
this.onRejectedList = [];
if (typeof fn !== "function") {
throw new TypeError(`Promise resolver ${typeof fn} is not a function`);
}
fn(this.resolve.bind(this), this.reject.bind(this));
}
resolve(data) {
if (this[Symbol.for("PromiseStatus")] == "pennding") {
this[Symbol.for("PromiseStatus")] = "fulfilled";
this[Symbol.for("PromiseValue")] = data;
for (let onFulfilled of this.onFulfilledList) {
onFulfilled && setTimeout(() => {
onFulfilled(data);
});
}
}
}
reject(reason) {
if (this[Symbol.for("PromiseStatus")] == "pennding") {
this[Symbol.for("PromiseStatus")] = "rejected";
this[Symbol.for("PromiseValue")] = reason;
for (let onRejected of this.onRejectedList) {
onRejected && setTimeout(() => {
onRejected(reason);
});
}
}
}
then(onFulfilled, onRejected) {
const status = this[Symbol.for("PromiseStatus")];
let nextPromise = null;
if (status == "pennding") {
nextPromise = new P((onFulfilledNext, onRejectedNext) => {
this.onFulfilledList.push(function (data) {
fulfill(onFulfilledNext, onRejectedNext, data);
});
this.onRejectedList.push(function (data) {
reject(onRejectedNext, data);
});
})
} else if (status == "fulfilled") {
nextPromise = new P((onFulfilledNext, onRejectedNext) => {
const data = this[Symbol.for("PromiseValue")];
try {
onFulfilled(data);
fulfill(onFulfilledNext, onRejectedNext, data);
} catch (error) {
onRejected(error);
reject(onRejectedNext, error);
}
})
} else if (status == "rejected") {
nextPromise = new P((onFulfilledNext, onRejectedNext) => {
const data = this[Symbol.for("PromiseValue")];
onRejected(data);
reject(onRejectedNext, data);
})
}
return nextPromise;
function fulfill(onFulfilledNext, onRejectedNext, data){
try {
if (typeof onFulfilled === 'function') {
const x = onFulfilled(data);
onFulfilledNext(x);
}else{
onFulfilledNext(data);
}
} catch (e) {
onRejectedNext(e);
}
}
function reject(onRejectedNext, data){
try {
if (typeof onRejected === 'function') {
const x = onRejected(data);
onRejectedNext(x);
}else{
onRejectedNext(data);
}
} catch (e) {
onRejectedNext(e);
}
}
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
}