ES6 Promise手写实现 & Proxy实际应用

ES6手写实现

  1. promise是一个类,它的构造函数接受一个函数,函数的两个参数也都是函数
  2. 在传入的函数中执行resolve表示成功,执行reject表示失败,传入的值会传给then方法的回调函数
  3. promise有一个叫做then的方法,该方法有两个参数,第一个参数是成功之后执行的回调函数,第二个参数是失败之后执行的回调函数。then方法在resolve或者reject执行之后才会执行,并且then方法中的值是传给resolve或reject的参数
  4. promise支持链式调用

成功、失败,这个很容易想到使用一个状态进行标记,实际上Promise就是这样做的。在Promise中使用了pending、fulfilled、rejected来标识当前的状态。

  • pending 初始状态,既不是成功,也不是失败状态。等待resolve或者reject调用更新状态。
  • fulfilled 意味着操作成功完成。
  • rejected 意味着操作失败。

需要注意的一点是,这三个状态之间只存在两个变换关系:

  • pending转换为fulfilled,只能由resolve方法完成转换
  • pending转换为rejected,只能由reject方法完成转换
type _Resolve = (value?: any) => any;
type _Reject = (reason?: any) => any;
type Executor = (_resolve: _Resolve, _reject: _Reject) => void;
type Then = (onFullfilled?: _Resolve, onRejected?: _Reject) => WPromise;
type Catch = (onRejected?: _Reject) => WPromise;
type Finally = (onHandler?: _Resolve) => WPromise;
type ParallelPromises = (iterable: WPromise[]) => WPromise;
type Resolve = (value: any) => WPromise;
type Reject = (reason: any) => WPromise;
interface Callback {
    onFullfilled?: _Resolve;
    onRejected?: _Reject;
    nextResolve: _Resolve;
    nextReject: _Reject;
}

class WPromise {
    private static PENDING = 'PENDING';
    private static FULFILLED = 'FULFILLED';
    private static REJECTED = 'REJECTED';

    private status = WPromise.PENDING;
    private value: any;
    private reason: any;
    private callbacks: Callback[] = [];
    private executed: boolean = false;

    constructor(executor: Executor) {
        executor(this._resolve.bind(this), this._reject.bind(this));
    }

    then: Then = (onFullfilled, onRejected) => {
        return new WPromise((nextResolve, nextReject) => {
            this._handler({ onFullfilled, onRejected, nextResolve, nextReject });
        });
    }

    catch: Catch = (onRejected) => {
        return this.then(undefined, onRejected);
    }

    finally: Finally = (onHandler) => {
        return this.then(onHandler, onHandler);
    }

    static resolve: Resolve = (value) => {
        if (WPromise._isPromiseLike(value)) {
            return value;
        }

        return new WPromise((resolve) => resolve(value));
    }

    static reject: Reject = (reason) => {
        if (WPromise._isPromiseLike(reason)) {
            return reason;
        }

        return new WPromise((resolve, reject) => reject(reason));
    }

    static all: ParallelPromises = (iterable) => {
        return new WPromise((resolve, reject) => {
            const ret: any[] = [];
            let count = 0;

            Array.from(iterable).forEach((item, index) => {
                WPromise.resolve(item).then(data => {
                    ret[index] = data;
                    count++;

                    if (count === iterable.length) {
                        resolve(ret);
                    }
                }, reject);
            });
        });
    }

    static race: ParallelPromises = (iterable) => {
        return new WPromise((resolve, reject) => {
            Array.from(iterable).forEach(item => {
                WPromise.resolve(item).then(resolve, reject);
            });
        });
    }

    private static _isPromiseLike = (data: any): boolean => {
        return data instanceof WPromise || (['object', 'function'].includes(typeof data) && 'then' in data);
    }

    private _handler = (callback: Callback) => {
        if (this.status === WPromise.PENDING) {
            this.callbacks.push(callback);
            return;
        }

        const { onFullfilled, onRejected, nextResolve, nextReject } = callback;

        if (this.status === WPromise.FULFILLED) {
            const toNextValue = onFullfilled ? onFullfilled(this.value) : this.value;
            nextResolve(toNextValue);
            return;
        }

        if (this.status === WPromise.REJECTED) {
            const toNextReason = onRejected ? onRejected(this.reason) : this.reason;
            nextReject(toNextReason);
        }
    }

    private _resolve: _Resolve = (value) => {
        if (this.executed) {
            return;
        }

        this.executed = true;

        // 链式调用时,针对下一个的promise
        // then方法返回一个promise时,value则返回promise执行的结果
        if (WPromise._isPromiseLike(value)) {
            value.then(this._resolve, this._reject);
            return;
        }

        this.value = value;
        this.status = WPromise.FULFILLED;

        this.callbacks.forEach(callback => this._handler(callback));
    }

    private _reject: _Reject = (reason) => {
        if (this.executed) {
            return;
        }

        this.executed = true;

        if (WPromise._isPromiseLike(reason)) {
            reason.then(this._resolve, this._reject);
            return;
        }

        this.reason = reason;
        this.status = WPromise.REJECTED;

        this.callbacks.forEach(callback => this._handler(callback));
    }
}

 

Proxy实现localStage代理

type SingleValue = string | number | boolean | null | undefined | object;
type Value = SingleValue | SingleValue[];
export type TargetType = Record<string, Value>;

/**
 *
 * @param storage  the storage used for store, localStorage or sessionStorage.
 * @param target the key and default values need store in the storage.
 * @param prefix  a prefix used for set unique storage key.
 * @returns
 */
export function createStorage<T extends TargetType>(storage: Storage, target: T) {
  // if the target  values is empty. throw error.
  if (Object.keys(target).length === 0) {
    throw Error('NO empty target!');
  }

  const handler: ProxyHandler<T> = {
    /**
     * set prop in storage
     * @returns boolean
     * @example
     * const storageProxy = createStorage(localStorage, '_');
     * storageProxy.a = 1;
     */
    set(target, prop: string, value) {
      if (!Reflect.has(target, prop)) {
        return false;
      }
      (target as TargetType)[prop] = value;
      storage.setItem(prop, JSON.stringify(value));
      return true;
    },

    /**
     * get prop from storage
     * @returns boolean
     * @example
     * const storageProxy = createStorage(localStorage,{a: ""}, '_');
     * console.log(storageProxy.a);
     */
    get(_, prop: string) {
      if (!Reflect.has(target, prop)) {
        return undefined;
      }
      const storageValue = storage.getItem(prop) || '';
      try {
        return JSON.parse(storageValue);
      } catch (error) {
        return storageValue;
      }
    },
    /**
     * delete single prop in storage
     * @returns boolean
     * @example
     * const storageProxy = createStorage(localStorage,{a: ""}, '_');
     * delete storageProxy.a;
     */
    deleteProperty(_, prop: string) {
      if (!Reflect.has(target, prop)) {
        return false;
      }
      storage.removeItem(prop);
      return true;
    },
    /**
     * clean storage
     * @returns boolean
     * @example
     * const storageProxy = createStorage(localStorage,{a: ""},'_');
     * Object.preventExtensions(storageProxy);
     */
    preventExtensions(target) {
      Object.preventExtensions(target);
      Object.keys(target).map((key) => storage.removeItem(key));
      return true;
    },
  };

  return new Proxy<T>(target, handler);
}