renrenche-fe / everything-about-front-end

探讨所有前端相关的知识点
MIT License
10 stars 0 forks source link

LazyMan 的实现 #7

Open sunhengzhe opened 6 years ago

sunhengzhe commented 6 years ago

这道题还没毕业的时候就看到过,这段时间突然又看到,联想到 promise,感觉还是很有意思

实现一个 LazyMan:

lazyMan('rick').eat('lunch').sleep(3000).eat('dinner');

输出:

Hi!This is rick!
eat lunch
// 等待 3 秒
eat dinner

另外如果先调用了 sleepFirst 方法,要首先进行 sleep

lazyMan('rick').sleepFirst(3000).eat('lunch');

输出:

// 等待 3 秒
Hi!This is rick!
eat lunch
sunhengzhe commented 6 years ago

单看 lazyMan('rick').eat('lunch').sleep(3000).eat('dinner'); 的调用可以发现 lazyman 的机制与 promise 非常相似,可以想到让 lazyman 成为一个 promise:

class Lazyman extends Promise {
    eat(food) {
        return this.then(function () {
            console.log(`eat ${food}`);
        });
    }

    sleep(time) {
        return this.then(function() {
            return new Lazyman((resolve, reject) => {
                setTimeout(resolve, time);
            });
        });
    }
}

function lazyman(name) {
    console.log(`Hi, my name is ${name}`);
    return Lazyman.resolve();
}

lazyman('Maz').eat('lunch').sleep(3000).eat('dinner');

这样就借助 promise 实现了一个 lazyman,不过缺点是实际上其实每次返回的都是另一个 lazyman 。。不太符合语义。

如果加上 sleepFirst 方法,打招呼的逻辑就不能直接输出了,可以考虑单独用一个 promise,借以打乱打招呼的顺序:

class Lazyman extends Promise {
    eat(food) {
        return this.then(() => {
            if (this.intro) {
                return this.intro().then(() => {
                    this.intro = null;
                });
            }
        }).then(function () {
            console.log(`eat ${food}`);
        });
    }

    sleep(time) {
        return this.then(function() {
            return new Lazyman((resolve, reject) => {
                setTimeout(resolve, time);
            });
        });
    }

    sleepFirst(time) {
        return this.then(() => {
            return new Lazyman((resolve, reject) => {
                setTimeout(() => {
                    this.intro().then(resolve)
                }, time);
            });
        });
    }
}

function lazyman(name) {
    const man = Lazyman.resolve();
    man.intro = () => new Promise((resolve, reject) => {
        console.log(`Hi, my name is ${name}`);
        resolve();
    })
    return man;
}

lazyman('Maz').sleepFirst(1000).eat('lunch').sleep(3000).eat('dinner');