javascript - 跨多个 promise 链保留数据

标签 javascript scope promise

我正在学习 Promise,所以我决定尝试扩展它们。一切正常,只是我很难弄清楚如何使值在所有函数中持久存在。

我的目标是从这个 promise 中找到被调用函数的计数,但是每次调用都会创建一个新函数,并且我很难找到一种传递该值的方法。我尝试添加一个构造函数,该构造函数将传递一个值中,但它似乎没有按照我的预期工作。我认为这是由于我误解了“this”的范围。

总而言之,我的每个函数“init”、“add”和“commit”都应该预先向“总步骤数”变量添加 1,在本例中现在是“i”。我想说的是,我正在进行 3 步中的第 1 步、3 步中的第 2 步等等......

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)}
);

最佳答案

正如您所指出的,链中没有一个 promise ,每个thencatch都返回一个新的 promise 。因此,不要尝试将状态保留在 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;
    }
    
<小时/>

我对这个问题的理解有点深入,但现在我明白了:您希望每次调用 init/ 时增加 s._progress.total添加/commit,并在每次then/catch回调时增加s._progress.count已调用。

这是一个简化的示例,仅使用 thencatch 而不是添加 initaddcommit 但如果您愿意,您可以轻松应用该模式来添加这些内容。

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

"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;
}

关于javascript - 跨多个 promise 链保留数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43384071/

相关文章:

javascript - 仅在脚本在 codeigniter 中完成执行后才重定向

javascript - 带异步等待和不带异步等待的 Firebase 数据库事务有什么区别?

c - #在多个文件中定义范围

javascript promise onSuccess 处理程序

jquery - 如何构建 "Last One Wins"jQuery-Ajax 队列?

javascript - jQuery生成输入框的方法

javascript - 返回 false 不适用于 MVC post

c# - 在 C# 中,我试图显示上一个 WriteLine 方法中的文本字符串。出现编译器错误

javascript - onsubmit 返回 validateForm 未触发

javascript - 即使出现错误,Promise 也会解决并触发下一个函数