EdwardZZZ / articles

工作点滴记录
2 stars 0 forks source link

Promise 实现 #28

Open EdwardZZZ opened 7 years ago

EdwardZZZ commented 7 years ago

链式

const PENDDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const type = (obj) => Object.prototype.toString.call(obj).slice(8, -1);

const _handle = Symbol('handle');
const _deferred = Symbol('deferred');
const _next = Symbol('next');

export default class Promise {
    constructor(fn) {
        if (!fn) {
            throw new TypeError("Promise resolver undefined is not a function");
        }

        const resolve = newValue => {
            if (newValue instanceof Promise) {
                newValue.then(resolve, reject);
                return;
            }
            this.status = FULFILLED;
            this.value = newValue;
            finale();
        };

        const reject = reason => {
            this.status = REJECTED;
            this.value = reason;
            finale();
        };

        const finale = () => {
            // setImmediate 为macroTask,此处应该用microTask实现
            // web用MutationObserver,node端用process.nextTick
            process.nextTick(this[_next]);
        };

        fn(resolve, reject);
    }

    status = PENDDING;
    value = void 0;

    [_deferred] = null;

    [_next] = (deferred = this[_deferred]) => {
        if (!deferred) return;
        this[_deferred] = null;

        const { didFulfill, didReject, resolve, reject } = deferred;
        let { status, value } = this;
        let callback = status === FULFILLED ? didFulfill : didReject;
        if (callback === null) {
            callback = status === FULFILLED ? resolve : reject;
            callback && callback(value);
            return;
        }

        try {
            if (type(callback) === 'AsyncFunction') return callback(value).then(resolve).catch(reject);

            const ret = callback(value);
            resolve && resolve(ret);
        } catch (e) {
            reject && reject(e);
        }
    }

    [_handle] = deferred => {
        this[_deferred] = deferred;

        if (this.status === FULFILLED) process.nextTick(this[_next]);
    };

    then(didFulfill = null, didReject = null) {
        return new Promise((resolve, reject) => {
            this[_handle]({ didFulfill, didReject, resolve, reject });
        });
    }

    catch(didReject = null) {
        return new Promise((resolve, reject) => {
            this[_handle]({ didFulfill: null, didReject, resolve, reject });
        });
    }

    static resolve = d => new Promise(resolve => resolve(d));
    static reject = d => new Promise((resolve, reject) => reject(d));
    static all = promises => new Promise((resolve, reject) => {
        const results = [];
        let n = 0;
        promises.forEach((promise, i) => {
            promise.then(data => {
                results[i] = data;
                if (++n === promises.length) {
                    resolve(results);
                }
            }, e => {
                reject(e);
            });
        });
    });
}

队列

const PENDDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const _handleList = Symbol('handleList');
const _handle = Symbol('handle');
const _next = Symbol('next');

const _resolve = Symbol('resolve');
const _reject = Symbol('reject');
const _finale = Symbol('finale');

class Promise {
    constructor(fn) {
        if (!fn) {
            throw new TypeError("Promise resolver undefined is not a function");
        }

        fn(this[_resolve], this[_reject]);
    }

    status = PENDDING;
    value = void 0;

    [_handleList] = [];

    [_resolve] = newValue => {
        if (newValue instanceof Promise) {
            newValue.then(this[_resolve], this[_reject]);
            return;
        }
        this.status = FULFILLED;
        this.value = newValue;
        this[_finale]();
    };

    [_reject] = reason => {
        this.status = REJECTED;
        this.value = reason;
        this[_finale]();
    };

    [_finale] = () => {
        process.nextTick(this[_next]);
    };

    [_next] = () => {
        const { [_handleList]: handleList, status, value } = this;
        if (status === PENDDING || handleList.length === 0) return;

        const { didFulfill, didReject } = handleList.shift();
        let callback = status === FULFILLED ? didFulfill : didReject;
        this.status = PENDDING;

        const { [_resolve]: resolve, [_reject]: reject } = this;
        if (!callback) {
            callback = status === FULFILLED ? resolve : reject;
            callback && callback(value);
            return;
        }

        try {
            const ret = callback(value);
            resolve && resolve(ret);
        } catch (e) {
            reject && reject(e);
        }
    }

    [_handle] = ({ didFulfill, didReject }) => {
        this[_handleList].push({ didFulfill, didReject });

        return this;
    };

    then(didFulfill = null, didReject = null) {
        return this[_handle]({ didFulfill, didReject });
    }

    catch(didReject = null) {
        return this[_handle]({ didFulfill: null, didReject });
    }

    static resolve = d => new Promise(resolve => resolve(d));
    static reject = d => new Promise((resolve, reject) => reject(d));
}
ynchuan commented 6 years ago

华叔,看了好久终于看懂了,但是有个小bug

new Promise((resolve, reject) => {
    setTimeout(function() {
        resolve('hhh2')
    }, 1000);
}).then((data) => {
    console.log(data);
    return new Promise((resolve, reject) => {
        setTimeout(function() {
            reject('ddd2');
        }, 1000);
    })
}).catch((data) => {
    console.log(data);
});

用例通不过,换成newValue.then(resolve, reject);就好了。 或者直接把

if (newValue.status === REJECTED) {
    newValue.catch(reject);
} else {
    newValue.then(resolve, reject);
}

整个换成newValue.then(resolve, reject);是不也行?

EdwardZZZ commented 6 years ago

@ynchuan 哈哈,之前写的有问题,被你发现啦。 确实可以用 newValue.then(resolve, reject); 就行,因为状态不同的时候会自动找对应的方法执行。

EdwardZZZ commented 4 years ago
const req = (str, fn) => {
    const time = Math.random() * 3e3;
    setTimeout(() => {
        if (time > 2e3) return fn(new Error(`time too long, ${str}, ${time}`));
        fn(null, `${str},${time}`);
    }, time);
}

const reqP = (str) => new Promise((resolve, reject) => {
    req(str, (err, _str) => {
        if (err) return reject(err);
        resolve(_str);
    });
});

let reqN = 0;
const waitPool = [];

function r(...props) {
    return new Promise((resolve, reject) => {
        const pThen = async (props1, resolve1, reject1) => {
            try {
                const data = await reqP(...props1);
                resolve1(data);
            } catch (err) {
                reject1(err);
            } finally {
                reqN--;
                if (waitPool.length > 0) {
                    const { props: props2, resolve: resolve2, reject: reject2 } = waitPool.shift();
                    reqN++;
                    pThen(props2, resolve2, reject2);
                }
            }
        }

        if (reqN < 3) {
            reqN++
            pThen(props, resolve, reject);
        } else {
            waitPool.push({ props, resolve, reject });
        }
    });
}

r('a').then(str => console.log(str)).catch(err => console.log(err.message));
r('b').then(str => console.log(str)).catch(err => console.log(err.message));
r('c').then(str => console.log(str)).catch(err => console.log(err.message));
r('d').then(str => console.log(str)).catch(err => console.log(err.message));
r('e').then(str => console.log(str)).catch(err => console.log(err.message));
r('f').then(str => console.log(str)).catch(err => console.log(err.message));
r('g').then(str => console.log(str)).catch(err => console.log(err.message));
r('h').then(str => console.log(str)).catch(err => console.log(err.message));
r('i').then(str => console.log(str)).catch(err => console.log(err.message));
EdwardZZZ commented 3 years ago
const p = () => new Promise((_, reject) => setTimeout(reject, 3e3));

(async () => {
    let i = 'a';
    try {
        i = await p().catch(err => {});

        console.log(1, i);
    } catch (err) {
        console.log(2, i);
    }
})();
EdwardZZZ commented 3 years ago

function test() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error(123));
        }, 1000);
    });
}

(async () => {
    await test().catch(err => {
        console.log(err);
    })

    console.log(123);
})();