import { asap } from "./asap";
/**
* 可重复执行resolve和reject的Promise
*/
export class MyPromise<T> {
private values: any[] = [];
private reasons: any[] = [];
/**
* 0 padding 1 resolve 2 reject
*/
private status: 0|1|2 = 0;
private onfulfilleds: ((value?: any) => void)[] = [];
private onrejecteds: ((reason?: any) => void)[] = [];
private onfinallys: ((value?: any) => void)[] = [];
constructor(exec: (resolve: (value?: T) => void, reject: (reason?: any) => void) => void) {
exec((value) => {
asap(() => {
this.status = 0;
this.values.push(value);
for (const onfulfilled of this.onfulfilleds) {
onfulfilled(value);
}
this.status = 1;
for (const onfinally of this.onfinallys) {
onfinally(value);
}
});
}, (reason) => {
asap(() => {
this.status = 0;
this.reasons.push(reason);
for (const onrejected of this.onrejecteds) {
onrejected(reason);
}
this.status = 2;
for (const onfinally of this.onfinallys) {
onfinally(reason);
}
});
});
}
static resolve<T>(value?: T): MyPromise<T> {
if (value instanceof MyPromise) {
return value;
}
return new MyPromise<T>((resolve) => {
resolve(value);
});
}
static reject<T>(reason?: T): MyPromise<T> {
if (reason instanceof MyPromise) {
return reason;
}
return new MyPromise<T>((resolve, reject) => {
reject(reason);
});
}
then<U>(onfulfilled?: (value?: T) => U, onrejected?: (reason: any) => U) {
return new MyPromise<U>((resolve, reject) => {
const wrapper_onfulfilled = onfulfilled ? this.wrapper_promise_func(onfulfilled, resolve, reject) : resolve;
const wrapper_onrejected = onrejected ? this.wrapper_promise_func(onrejected, resolve, reject) : reject;
asap(() => {
if (this.status == 1) {
for (const value of this.values) {
wrapper_onfulfilled(value);
}
} else if (this.status == 2) {
for (const reason of this.reasons) {
wrapper_onrejected(reason);
}
}
this.onfulfilleds.push(wrapper_onfulfilled);
this.onrejecteds.push(wrapper_onrejected);
});
});
}
catch<U>(onrejected?: (reason: any) => U) {
return this.then(undefined, onrejected);
}
finally(onfinally?: () => void) {
return this.then(value => {
onfinally();
return value;
}, reason => {
onfinally();
throw reason;
});
}
private wrapper_promise_func(func: (value?: any) => any, resolve, reject) {
return (value?: any) => {
try {
const ret = func(value);
if (ret === this) {
reject(new Error('value can not equal self'));
return;
}
if (ret instanceof MyPromise) {
if (ret.status == 1) {
for (const value of ret.values) {
resolve(value);
}
} else if (ret.status == 2) {
for (const reason of ret.reasons) {
reject(reason);
}
}
ret.onfulfilleds.push(resolve);
ret.onrejecteds.push(reject);
return;
}
resolve(ret);
} catch (error) {
console.error(error);
reject(error);
}
};
}
}
asap.js根据环境选择异步调用方法
let len = 0;
let vertxNext;
let customSchedulerFn;
export var asap = function asap(callback, arg) {
queue[len] = callback;
queue[len + 1] = arg;
len += 2;
if (len === 2) {
// If len is 2, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
if (customSchedulerFn) {
customSchedulerFn(flush);
} else {
scheduleFlush();
}
}
}
export function setScheduler(scheduleFn) {
customSchedulerFn = scheduleFn;
}
export function setAsap(asapFn) {
asap = asapFn;
}
const browserWindow = (typeof window !== 'undefined') ? window : undefined;
const browserGlobal = browserWindow || {};
const BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
const isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
// test for web worker but not in IE10
const isWorker = typeof Uint8ClampedArray !== 'undefined' &&
typeof importScripts !== 'undefined' &&
typeof MessageChannel !== 'undefined';
// node
function useNextTick() {
// node version 0.10.x displays a deprecation warning when nextTick is used recursively
// see https://github.com/cujojs/when/issues/410 for details
return () => process.nextTick(flush);
}
// vertx
function useVertxTimer() {
if (typeof vertxNext !== 'undefined') {
return function() {
vertxNext(flush);
};
}
return useSetTimeout();
}
function useMutationObserver() {
let iterations = 0;
const observer = new BrowserMutationObserver(flush);
const node = document.createTextNode('');
observer.observe(node, { characterData: true });
return () => {
node.data = (iterations = ++iterations % 2);
};
}
// web worker
function useMessageChannel() {
const channel = new MessageChannel();
channel.port1.onmessage = flush;
return () => channel.port2.postMessage(0);
}
function useSetTimeout() {
// Store setTimeout reference so es6-promise will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
const globalSetTimeout = setTimeout;
return () => globalSetTimeout(flush, 0);
}
const queue = new Array(1000);
function flush() {
for (let i = 0; i < len; i+=2) {
let callback = queue[i];
let arg = queue[i+1];
callback(arg);
queue[i] = undefined;
queue[i+1] = undefined;
}
len = 0;
}
function attemptVertx() {
try {
const vertx = Function('return this')().require('vertx');
vertxNext = vertx.runOnLoop || vertx.runOnContext;
return useVertxTimer();
} catch(e) {
return useSetTimeout();
}
}
let scheduleFlush;
// Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) {
scheduleFlush = useMutationObserver();
} else if (isWorker) {
scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && typeof require === 'function') {
scheduleFlush = attemptVertx();
} else {
scheduleFlush = useSetTimeout();
}