跨多个承诺链保留数据

时间:2017-04-13 05:18:25

标签: javascript scope promise

我正在学习承诺,所以我决定继续扩展它们。一切正常,除非我很难弄清楚如何在所有功能中保持价值。

我的目标是从这个承诺中找到被调用函数的计数,但是每次调用都会创建一个新函数,而我却无法找到传递值的方法。我尝试添加一个构造函数,它将传递一个值在,但它似乎没有像我期望的那样工作。我认为这是由于我误解了"这个"。

的范围

总结一下,我的每个功能" init","添加"并且"承诺"所有预先都应该在"步骤总数和#34;变量,现在在这个例子中是" i"。我希望能够说,我在第3步,第3步,第3步等等......

class Repo {
    constructor(setup) {
        this.s = {};
        this.s._progress = { "total":0, "count":0 };
        this.s._logging = { "enabled":true, "location":"console", "pretty":true, "verbose":false, "tabCount":0 };
        this.s._remoteInfo = { "name":"", "url":"" };
        this.s._localInfo = { "path":"" };

        this.s._logReset = () => {
            this.s._logging.tabCount = 0;
        };

        this.s._log = (message, tabCount) => {
            if(this.s._logging.enabled) {
                let tabs = '';

                if(this.s._logging.pretty) {
                    for(let i = 0; i < tabCount; i++) { tabs = tabs + '\t' };
                }

                if(this.s._logging.location == 'console') { console.log(tabs, message); }
                else {
                    //TODO: implement the file location to output
                }
            }
        };

        this.s._progressReset = () => {
            this.s._progress.total = 0;
            this.s._progress.count = 0;
        };

        this.s._addProgressTotal = () => {
            this.s._progress.total++;
            console.log(this.s._progress.total)
        }

        this.s._addProgress = () => {
            this.s._progress.count++;
            console.log('Progress is ' + this.s._progress.count + ' out of ' + this.s._progress.total)
        }
    }

    //Starts the promise chain and passes in the settings to be used.
    start() {
        this.s._logReset();
        this.s._progressReset();

        return new RepoPromise((resolve, reject) => {
            this.s._log('Start Log: <time>',0)
            resolve(this.s);
        });
    }
}

class RepoPromise extends Promise {
    constructor(executor, val) {
        let e = executor || function (res, rej) { res('')};
        super((resolve, reject) => {
            return e(resolve, reject);
        });

        this.i = val || 0;
    }

    end() {
        const returnValue = super.then((s) => {
            return new RepoPromise((resolve, reject) => {
                s._log('End Log: <time>',0)
                resolve(s);
            }, this.i);
        });
        return returnValue;
    }

    init() {
        //I know I need to add 1 to "i" here, but it won't work
        const returnValue = super.then((s) => {
            return new RepoPromise((resolve, reject) => {
                s._log('git init',1);
                s._addProgress();
                resolve(s, '')
            }, ++this.i);
        });
        return returnValue;
    };

    add() {
        //I know I need to add 1 to "i" here, but it won't work
        const returnValue = super.then((s) => {
            return new RepoPromise((resolve, reject) => {
                setTimeout(() => {
                    s._log('git add',1);
                    s._addProgress();
                    resolve(s,'');
                    //reject('Add Failed')
                }, Math.random() * (10000 - 1000) + 1000);
            },++this.i);
        });
        return returnValue;
    }

    commit() {
        //I know I need to add 1 to "i" here, but it won't work
        const returnValue = super.then((s) => {
            return new RepoPromise((resolve, reject) => {
                setTimeout(() => {
                    s._log('git commit -m "message"',1);
                    s._addProgress();
                    resolve(s, 'Finished');
                }, Math.random() * (5000 - 1000) + 1000);
            }, ++this.i);
        });
        return returnValue;
    }

    then(onFulfilled, onRejected) {
        const returnValue = super.then(onFulfilled, onRejected);
        return returnValue;
    }
}

用法:

var p = new Repo('')
.start()
    .init()
    .add()
    .commit()
.end()
.catch(
    x => {console.log('it broke: ' + x)}
);

1 个答案:

答案 0 :(得分:1)

正如您所指出的,链中没有一个承诺,每个thencatch都会返回承诺。因此,请勿尝试保留RepoPromise中的状态,将其保留在您通过链的对象中作为分辨率值:s

将你的第二个参数重新设置为RepoPromise构造函数:你无法可靠地执行此操作,因为每次调用构造函数时都无法控制。请记住,当您致电thencatch时,系统会调用该构造函数。这是在s上传递价值的另一个原因。 :-)为了完整起见,这里说明了在Promise内调用构造函数的事实:

class MyPromise extends Promise {
  constructor(...args) {
    super(...args);
    console.log("MyPromise constructor called");
  }
}

MyPromise.resolve()
  .then(val => val)
  .then(val => val)
  .then(val => val);

几个旁注:

  1. 此:

    super((resolve, reject) => {
        return e(resolve, reject);
    });
    

    可以简单地写成:

    super(e);
    
  2. 这没有做任何事情,可以删除:

    then(onFulfilled, onRejected) {
        const returnValue = super.then(onFulfilled, onRejected);
        return returnValue;
    }
    
  3. 我对这个问题的理解有点密集,但现在我明白了:您希望每次拨打s._progress.total / init / add时增加commit,在调用每个s._progress.count / then回调时增加catch

    以下是仅使用thencatch而不是添加initaddcommit的简化示例,但您可以轻松地将模式应用于如果你愿意,可以添加它们。

    解决方案是将状态跟踪器(s)保留在promise对象上,并将自己插入创建新promises的各种方式(thencatch),以便我们复制追踪从旧承诺到新承诺。我们在所有这些承诺中共享跟踪器,例如,来自根承诺的跟踪器跟踪从那里开始的所有内容。见评论:

    "use strict";
    
    // For tracking our status
    class Status {
        constructor(total = 0, count = 0) {
            this.id = ++Status.id;
            this.total = total;
            this.count = count;
        }
        addCall() {
            ++this.total;
            return this;
        }
        addProgress() {
            ++this.count;
            return this;
        }
        toString() {
            return `[S${this.id}]: Total: ${this.total}, Count: ${this.count}`;
        }
    }
    Status.id = 0;
    
    // The promise subclass
    class RepoPromise extends Promise {
        constructor(executor) {
            super(executor);
            this.s = new Status();
        }
        // Utility method to wrap `then`/`catch` callbacks so we hook into when they're called
        _wrapCallbacks(...callbacks) {
            return callbacks.filter(c => c).map(c => value => this._handleCallback(c, value));
        }
        // Utility method for when the callback should be called: We track that we've seen
        // the call then execute the callback
        _handleCallback(callback, value) {
            this.s.addProgress();
            console.log("Progress: " + this.s);
            return callback(value);
        }
        // Standard `then`, but overridden so we track what's going on, including copying
        // our status object to the new promise before returning it
        then(onResolved, onRejected) {
            this.s.addCall();
            console.log("Added: " + this.s);
            const newPromise = super.then(...this._wrapCallbacks(onResolved, onRejected));
            newPromise.s = this.s;
            return newPromise;
        }
        // Standard `catch`, doing the same things as `then`
        catch(onRejected) {
            this.s.addCall();
            console.log("Added: " + this.s);
            const newPromise = super.catch(...this._wrapCallbacks(onRejected));
            newPromise.s = this.s;
            return newPromise;
        }
    }
    
    // Create a promise we'll resolve after a random timeout
    function delayedGratification() {
        return new Promise(resolve => {
            setTimeout(_ => {
                resolve();
            }, Math.random() * 1000);
        });
    }
    
    // Run! Note we follow both kinds of paths: Chain and diverge:
    const rp = RepoPromise.resolve();
    rp.then(delayedGratification)   // First chain
      .then(delayedGratification)
      .then(delayedGratification);
    rp.catch(delayedGratification)  // Second chain
      .then(delayedGratification);
    .as-console-wrapper {
      max-height: 100% !important;
    }