手写一个简单版的 Promise

86 阅读1分钟

ECMAScript 6 新增的 Promise,可以通过 new 操作符来实例化一个期约实例,Promise 异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。 最近尝试手写一个简单 Promise,也可以说是一个残缺版的 Promise,因为这里并没有实现 then 之后的进一步返回的一个新的 Promise(其实也是我现在还没想明白这里如何实现),也没有 finally 等这一个功能。只是简单的写了下我理解的最基本的实现而已。

class Promise {
    constructor(fn) {
        this.state = 0;
        this.value = null;
        this.deferreds = [];

        doResolve(fn, this);
    }

    then(onFulfilled, onRejected) {
        handle(this, { onFulfilled, onRejected });
    }

    catch(onRejected) {
        handle(this, { onRejected });
    }
}

function doResolve(fn, promise) {
    try {
        fn((result) => {
            resolve(promise, result);
        }, (reason) => {
            reject(promise, reason);
        });
    } catch (error) {
        reject(promise, error);
    }
}

function resolve(promise, result) {
    if (promise.state !== 0) {
        return;
    }

    promise.state = 1;
    promise.value = result;

    finale(promise);
}

function reject(promise, reason) {
    if (promise.state !== 0) {
        return;
    }

    promise.state = 2;
    promise.value = reason;

    finale(promise);
}

function finale(promise) {
    promise.deferreds.forEach((_promise) => {
        if (promise.state === 1) {
            _promise.onFulfilled(promise.value);
        } else {
            _promise.onRejected(promise.value);
        }
    });
    promise.deferreds = [];
}

function handle(promise, deferred) {
    if (promise.state === 0) {
        promise.deferreds.push(deferred);
        return;
    }

    handleResolved(promise, deferred);
}

function handleResolved(promise, deferred) {
    const thenFn = promise.state === 1 ? deferred.onFulfilled : deferred.onRejected;
    if (!thenFn) {
        if (promise.state === 1) {
            resolve(deferred.promise, promise.value);
        } else {
            reject(deferred.promise, promise.value);
        }
        return;
    }

    let thenRes = null;
    try {
        thenRes = thenFn(promise.value);
    } catch (error) {
        thenRes = error;
    }
}