Promise是一种异步编程解决方案,可以使异步代码更加优雅。
例如,我们需要进行这么一个操作:
- 向一个url获取一个名字
- 根据这个名字获取一个数据
- 根据这个数据获取到我们需要结果
使用回调函数:
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+规范的一种实现。(Promise A+ 规范翻译)
现在试着自己实现一个Promise。
首先一个promise拥有属性状态,初始时为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的状态分别迁移为Fulfilled或Rejected。promise的状态只能改变一次。
然后promise构造函数接收一个函数作为参数,并往该函数传入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异步执行,resove或reject被调用时不能马上调用,需要在当前一轮事件循环结束后再调用onFulfilled/onRejected。可通过setTimeout来实现
resolve(data) {
if (this[Symbol.for("PromiseStatus")] == "pennding") {
this[Symbol.for("PromiseStatus")] = "fulfilled";
this[Symbol.for("PromiseValue")] = data;
setTimeout(() => {
this.onFulfilled(data);
});
}
}
then方法可以被连续调用,所以需要增加onFulfilledList、onRejectedList两个数组,再resolve/reject被调用时遍历数组执行。
最后,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);
}
}