Promise 基本结构
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("FULFILLED");
}, 1000);
});
p1.then(() => {}, () => {});
构造函数 Promise
必须接受一个函数作为参数,同时这个函数又包含 resolve
和 reject
两个参数,这两个参数同样是函数。
Promise
对象存在三种状态,Pending(进行中)
,Fulfilled(已完成)
,Rejected(已完成)
。
resolve
的作用就是将 Promise
对象的状态从 Pending
转换为 Fulfilled
。同时传递一个值出去。
reject
则是将 Promise
对象的状态从 Pending
转换为 Rejected
。同时传递一个值(可以是错误)出去。
Promise
实例生成后,可以使用 then
指定 Fulfilled
状态和 Rejected
状态的回调函数。
我们定义一个函数,用来判断一个变量是否是函数——
const isFunction = variable => typeof variable === "function";
同时定义一个 MyPromise
的类——
const isFunction = variable => typeof variable === "function";
class MyPromise {
constructor(fn) {
if (!isFunction(fn)) {
throw new Error("MyPromise must accept a function as a parameter");
}
}
then(onFulfilled, onRejected) {}
}
定义 Promise
的三种状态,我们可以借助 ES6 的 Symbol
对象,来生成一个唯一标识的值——
const PENDING = Symbol("pending");
const FULFILLED = Symbol("fulfilled");
const REJECTED = Symbol("rejected");
Symbol
生成的值必定是独一无二的,Symbol
函数的的参数仅仅作为控制台输出时的解释说明,并不会影响到 Symbol
的返回值的内容。
为 MyPromise
添加状态和值——
const isFunction = variable => typeof variable === "function";
const PENDING = Symbol("pending");
const FULFILLED = Symbol("fulfilled");
const REJECTED = Symbol("rejected");
class MyPromise {
constructor(fn) {
if (!isFunction(fn)) {
throw new Error("MyPromise must accept a function as a parameter");
}
this.status = PENDING;
this.value = undefined;
}
then(onFulfilled, onRejected) {}
}
但是这样会出现一个问题,MyPromise
实例化后能够从类的外部获得 status
和 value
,并且能够从外界进行篡改。一般来说,应该将这个值设置为私有属性。
ES6 中 Class 并不支持私有属性,虽然有一个提案是使用 #
前缀来表示这个值为私有属性,但是有些还不能支持(虽然 babel 已经支持了)
设置私有属性有几种方法——
- 使用 # ,但是在没有 Babel 的情况下无法使用
class MyClass {
#x;
constructor(v) {
#x = v;
}
}
- 在类外声明一个对象,用对象存储属性,一般使用 ES6 的
WeakMap
,因为WeakMap
的键名所引用的对象都是弱引用,即垃圾回收机制不会将该引用考虑在内(引用计数垃圾收集,不会将存储在WeakMap
中的内容认为是被WeakMap
引用)(
const __ = new WeakMap();
function get(that, key) {
try {
return __.get(that)[key];
} catch (e) {
console.error(e);
return undefined;
}
}
function set(that, key, value) {
const lastValue = __.get(that);
const newValue = {
...lastValue,
[key]: value
};
__.set(that, newValue);
}
class MyClass {
constructor(v) {
set(this, x, v); // this.x = v; 但是这个 x 是私有属性,外界无法得到
}
anyWay() {
return get(this, x);
}
}
- 借助
Symbol
,类外因为无法拿到这个Symbol
返回值所以无法使用该属性
const x = Symbol("x");
class MyClass {
constructor(v) {
this[x] = v;
}
anyWay() {
return this[x];
}
}
这里我们采取第二种方法
const isFunction = variable => typeof variable === "function";
const __ = new WeakMap();
function get(that, key) {
try {
return __.get(that)[key];
} catch (e) {
console.error(e);
return undefined;
}
}
function set(that, key, value) {
const lastValue = __.get(that);
const newValue = {
...lastValue,
[key]: value
};
__.set(that, newValue);
}
const PENDING = Symbol("pending");
const FULFILLED = Symbol("fulfilled");
const REJECTED = Symbol("rejected");
const promiseStatus = Symbol("status");
const promiseValue = Symbol("value");
class MyPromise {
constructor(fn) {
if (!isFunction(fn)) {
throw new Error("MyPromise must accept a function as a parameter");
}
set(this, promiseStatus, PENDING);
set(this, promiseValue, undefined);
}
then(onFulfilled, onRejected) {}
}
因为 then
有两个参数,onFulfilled
和 onRejected
,都是可选参数。
如果 onFulfilled
和 onRejected
都不是函数,其必须被忽略。
如果 onFulfilled
是函数,当 Promise
状态变为成功时必定调用,且第一个参数应该是 Promise
成功状态 resolve(val)
是传入的值(即 value
);在 Promise
状态改变前其不可被调用;被调用的次数不超过一次。
onRejected
同理。
then
方法可以被同一个 Promise
对象调用多次,而且所有的 onFulfilled
或 onRejected
会按照注册顺序依次回调。
const p1 = new Promise(() => {});
p1.then(() => {});
p1.then(() => {});
then
必须返回一个新的 Promise
,支持链式调用。
const p1 = new Promise(() => {});
const p2 = p1.then(() => {}).then(() => {});
Promise
的执行规则——
值的传递
如果 onFulfilled
或者 onRejected
返回一个 x
——
-
x
为Promise
,则后一个回调函数,就会等待该Promise
对象的状态改变,才会被调用,并且新的Promise
状态和x
的状态相同。 -
x
不为Promise
,则将x
直接作为新返回的Promise
对象的传递出去的值,即新的onFulfilled
或者onRejected
的参数。
const p1 = new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
p1.then(() => {
return "普通值";
}).then(res => {
console.log(res); // 普通值
});
p1.then(() => {
return new Promise(resolve => {
setTimeout(() => {
resolve("Promise");
}, 1000);
});
}).then(res => {
console.log(res); // Promise
});
但如果在 onFulfilled
或者 onRejected
中抛出一个异常 e
,则 then
返回的 Promise
的状态将变为 Rejected
,并传递出 e
const p1 = new Promise(resolve => {
setTimeout(() => {
resolve("success");
}, 1000);
})
.then(() => {
throw new Error("这里抛出一个异常");
})
.then(
res => {
console.log(res);
},
err => {
console.log(err); // 这里抛出一个异常
}
);
因为可能出现【使用 then
绑定的 onFulfilled
和 onRejected
的时候,Promise
的状态依旧是 Pending
】的情况,所以我们需要使用两个数组去维护他们,同样借助 WeakMap
去维护他们
const isFunction = variable => typeof variable === "function";
const __ = new WeakMap();
function get(that, key) {
try {
return __.get(that)[key];
} catch (e) {
console.error(e);
return undefined;
}
}
function set(that, key, value) {
const lastValue = __.get(that);
const newValue = {
...lastValue,
[key]: value
};
__.set(that, newValue);
}
const PENDING = Symbol("pending");
const FULFILLED = Symbol("fulfilled");
const REJECTED = Symbol("rejected");
const promiseStatus = Symbol("status");
const promiseValue = Symbol("value");
const fulfilledCallbackQueue = Symbol("fulfilledCallbackQueue");
const rejectedCallbackQueue = Symbol("rejectedCallbackQueue");
const resolve = Symbol("resolve");
const reject = Symbol("reject");
class MyPromise {
constructor(fn) {
if (!isFunction(fn)) {
throw new Error("MyPromise must accept a function as a parameter");
}
set(this, promiseStatus, PENDING);
set(this, promiseValue, undefined);
set(this, fulfilledCallbackQueue, []);
set(this, rejectedCallbackQueue, []);
try {
fn(this[resolve].bind(this), this[reject].bind(this));
} catch (e) {
this[reject](e);
}
}
[resolve](val) {}
[reject](err) {}
then(onFulfilled, onRejected) {}
}
因为实例化 Promise
时传入的回调函数包含 resolve
和 reject
,这两个应该在 Promise
中自己实现,并且在 Promise
的构造函数中调用这个回调函数并且传入 resolve
和 reject
。
这里就涉及到 ES6 中 Class
构建私有方法的问题,一般来说有以下几种方法——
- 命名上区分,即在命名前面加一个
_
表示这应该是一个私有方法,而且不希望外界能够调用这个方法
class MyClass {
_anyWay() {}
}
但其实类的外部依旧可以调用这个方法。
- 将私有方法移出类
function anyWay() {}
class MyClass {
foo() {
anyWay.call(this);
}
}
- 借助
Symbol
const anyWay = Symbol("private func");
class MyClass {
[anyWay]() {}
}
我们这里采取的是第 3 种方案
const isFunction = variable => typeof variable === "function";
const __ = new WeakMap();
function get(that, key) {
try {
return __.get(that)[key];
} catch (e) {
console.error(e);
return undefined;
}
}
function set(that, key, value) {
const lastValue = __.get(that);
const newValue = {
...lastValue,
[key]: value
};
__.set(that, newValue);
}
const promiseStatus = Symbol("status");
const promiseValue = Symbol("value");
const fulfilledCallbackQueue = Symbol("fulfilledCallbackQueue");
const rejectedCallbackQueue = Symbol("rejectedCallbackQueue");
const PENDING = Symbol("pending");
const FULFILLED = Symbol("fulfilled");
const REJECTED = Symbol("rejected");
const resolve = Symbol("resolve");
const reject = Symbol("reject");
class MyPromise {
constructor(fn) {
if (!isFunction(fn)) {
throw new Error("MyPromise must accept a function as a parameter");
}
set(this, promiseStatus, PENDING);
set(this, promiseValue, undefined);
set(this, fulfilledCallbackQueue, []);
set(this, rejectedCallbackQueue, []);
try {
fn(this[resolve].bind(this), this[reject].bind(this));
} catch (e) {
this[reject](e);
}
}
[resolve](val) {
const run = () => {
if (get(this, promiseStatus) !== PENDING) return;
const runFulfilled = value => {
let cb;
while ((cb = get(this, fulfilledCallbackQueue).shift())) {
cb(value);
}
};
const runRejected = error => {
let cb;
while ((cb = get(this, rejectedCallbackQueue).shift())) {
cb(error);
}
};
if (val instanceof MyPromise) {
val.then(
value => {
set(this, promiseValue, value);
set(this, promiseStatus, FULFILLED);
runFulfilled(value);
},
err => {
set(this, promiseValue, err);
set(this, promiseStatus, REJECTED);
runRejected(err);
}
);
} else {
set(this, promiseValue, val);
set(this, promiseStatus, FULFILLED);
runFulfilled(val);
}
};
setTimeout(run, 0);
}
[reject](err) {
if (get(this, promiseStatus) !== PENDING) return;
const run = () => {
set(this, promiseStatus, REJECTED);
set(this, promiseValue, err);
let cb;
while ((cb = get(this, rejectedCallbackQueue).shift())) {
cb(err);
}
};
setTimeout(run, 0);
}
then(onFulfilled, onRejected) {
return new MyPromise((onFulfilledNext, onRejectedNext) => {
const fulfilled = value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value);
} else {
const res = onFulfilled(value);
if (res instanceof MyPromise) {
res.then(onFulfilledNext, onRejectedNext);
} else {
onFulfilledNext(res);
}
}
} catch (err) {
onRejectedNext(err);
}
};
const rejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error);
} else {
const res = onRejected(error);
if (res instanceof MyPromise) {
res.then(onFulfilledNext, onRejectedNext);
} else {
onFulfilledNext(res);
}
}
} catch (err) {
onRejectedNext(err);
}
};
switch (get(this, promiseStatus)) {
case PENDING:
const newFulfilledCallbackQueue = [
...get(this, fulfilledCallbackQueue),
fulfilled
];
set(this, fulfilledCallbackQueue, newFulfilledCallbackQueue);
const newRejectedCallbackQueue = [
...get(this, rejectedCallbackQueue),
rejected
];
set(this, rejectedCallbackQueue, newRejectedCallbackQueue);
break;
case FULFILLED:
fulfilled(get(this, promiseValue));
break;
case REJECTED:
rejected(get(this, promiseValue));
break;
}
});
}
}