实现Promise
const STATUS = {
PENDING: 'PENDING',
FULFILLED: 'FULFILLED',
REJECTED: 'REJECTED',
}
class Promise {
constructor(executor) {
this.status = STATUS.PENDING;
this.value = null;
this.reason = null;
this.onResolvedList = [];
this.onRejectList = [];
const resolve = (value) => {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
this.status = STATUS.FULFILLED;
this.value = value;
this.onResolvedList.forEach(cb => {
cb && cb();
});
}
const reject = (reason) => {
this.status = STATUS.REJECTED;
this.reason = reason;
this.onRejectList.forEach(cb => {
cb && cb();
});
}
try {
executor && executor(resolve, reject);
} catch (error) {
reject(error);
}
}
static resolve (value) {
return new Promise((resolve, reject) => {
resolve(value);
});
}
static reject (value) {
return new Promise((resolve, reject) => {
reject(value);
});
}
// 1. x === promise(它本身) => error
// 2. x => value => value
// 3. x 是 promise =>
// 3.1 => x.then =>
// 3.1.1 => function => value or error => resolve(value) or reject(error)
// 3.1.2 => value => value
resolvePromise (promise, x, resolve, reject) {
if (x === promise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
let called;
const beCalled = called => {
if (called) {
return;
}
called = true;
}
if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) {
const then = x.then;
try {
if (typeof then === 'function') {
then.call(
x,
y => {
beCalled(called);
resolvePromise(promise, y, resolve, reject);
},
e => {
beCalled(called);
reject(e);
}
);
} else {
resolve(x);
}
} catch (error) {
beCalled(called);
reject(error);
}
} else {
resolve(x);
}
}
then (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function'
? onFulfilled
: value => value;
onRejected = typeof onRejected === 'function'
? onRejected
: error => {
throw error
};
const promise = new Promise((resolve, reject) => {
if (this.status === STATUS.FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
this.resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
}
if (this.status === STATUS.REJECTED) {
try {
setTimeout(() => {
try {
const x = onRejected(this.reason)
this.resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} catch (error) {
reject(error);
}
}
if (this.status === STATUS.PENDING) {
try {
this.onResolvedList.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
this.resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.onRejectList.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason)
this.resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
} catch (error) {
reject(error);
}
}
});
return promise;
}
}
Promise.prototype.catch
Promise.prototype.catch = cb => {
if (typeof cb !== 'function') {
return Promise.reject(new TypeError(`${cb} is not a function`));
}
return this.then(null, cb);
}
Promise.prototype.finally
Promise.prototype.finally = cb => {
if (typeof cb !== 'function') {
return Promise.reject(new TypeError(`${cb} is not a function`));
}
return this.then(
value => Promise.resolve(cb()).then(() => value),
reason => Promise.resolve(cb()).then(() => { throw reason; }),
);
}
Promise.prototype.retry
Promise.prototype.retry = (cb, times = 1) => {
if (typeof cb !== 'function') {
return Promise.reject(new TypeError(`${cb} is not a function`));
}
return new Promise((resolve, reject) => {
while(times--) {
try {
Promise.resolve(cb()).then(value => {
resolve(value);
break;
});
} catch (error) {
console.log(`Retry in the ${times}\'s timers.`)
}
}
}).catch (error) {
reject(error);
}
}
Promise.race
Promise.race = values => {
if (!Array.isArray(values)) {
const type = typeof values;
return new TypeError(`TypeError: ${type} ${values} is not iterable`)
}
return new Promise((resolve, reject) => {
for (let i = 0; i < values.length; i++) {
const value = values[i];
Promise.resolve(value).then(
val => resolve(val),
reject
);
// if (typeof value === 'function') {
// value.then(val => resolve(val), reject);
// } else {
// resolve(value);
// }
}
});
}
Promise.all
Promise.all = values => {
if (!Array.isArray(values)) {
const type = typeof values;
return new TypeError(`TypeError: ${type} ${values} is not iterable`)
}
return new Promise((resolve, reject) => {
const res = [];
const order = 0;
const processed = (value, i) => {
res[i] = value;
if (++order === values.length) {
resolve(res);
}
}
for (let i = 0; i < values.length; i++) {
const value = values[i];
Promise.resolve(value).then(
val => processed(val, i),
reject
);
// if (typeof value === 'function') {
// value.then(value => processed(value, i), reject);
// } else {
// processed(value, i);
// }
}
});
}
Promise.allSettled
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
Promise.allSettled = promises => {
if (!Array.isArray(promises)) {
const type = typeof promises;
return new TypeError(`TypeError: ${type} ${promises} is not iterable`)
}
if (promises.length === 0) {
return Promise.resolve([]);
}
return Promise.all(promises.map(promise => {
return Promise.resolve(promise).then(
value => ({ status: 'fulfilled', value }),
reason => ({ status: 'rejected', reason }),
);
}));
}
Promise.any
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/any
Promise.any = promises => {
if (!Array.isArray(promises)) {
const type = typeof promises;
return new TypeError(`TypeError: ${type} ${promises} is not iterable`)
}
if (promises.length === 0) {
return Promise.reject(new TypeError('any(): array must not be empty'));
}
return new Promise((resolve, reject) => {
let index = 0;
const rejected = [];
const onRejected = (reason, i) => {
index++;
rejected[index] = reason;
if (index === promises.length) {
reject(new AggregateError('No Promise in Promise.any was resolved'));
}
}
for (let i = 0; i < promises.length; i++) {
const value = promises[i];
Promise.resolve(value).then(
resolve,
reason => onRejected(reason, i)
);
}
});
}
wrapAbort
// 中断
function wrapAbort (promise) {
let abort;
const abortPromise = new Promise((resolve, reject) => {
abort = reject;
});
let p = Promise.race([promise, abortPromise]);
return p;
}
promisify
function promisify (fn) {
return (...args) => {
return new Promise((resolve, reject) => {
fn(
...args,
(reason, value) => {
if (reason) {
reject(reason);
}
resolve(value);
}
);
})
}
}
官方用例测试
npm i -D promises-aplus-tests
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
测试
const promise = new Promise((resolve, reject) => {
reject('失败');
}).then().then().then(data => {
console.log(data);
}, err => {
console.log('err', err);
})