【问题标题】:Sequencing async operations followed by onResult call对异步操作进行排序,然后进行 onResult 调用
【发布时间】:2014-12-20 14:20:08
【问题描述】:

小提琴:http://jsfiddle.net/smartdev101/eLxxpjp3/

在 asyncAction 函数调用中,已创建一个 Promise 来对两个异步操作进行排序,getRecordsgetTotal,然后在成功时最终调用 onResult(data) 或在异常情况下调用 onFail(info)

两个异步操作完成后如何调用 onResult?

asyncAction: function(url, params, resultFunction, faultFunction) {
    puremvc.asyncproxy.AsyncProxy.prototype.asyncAction.call(this, resultFunction, faultFunction);

    if(!params.id) { //get master
        var queryString = this.url.parse(url, true).query;
        var offset = queryString.offset ? Number(queryString.offset) : 0;
        var limit = queryString.limit ? Number(queryString.limit) : 20;

        var data = {rows: null, total: null};

        var self = this;
        this.getRecords(data, offset, limit)
        .then(function(data){return self.getTotal(data)})
        //.fail(function(error){self.onFault(error)})
        .done(function(data){self.onResult(data)})

    } else { //get detail
        this.getDetail(params.id);
    }
},

getRecords: function(data, offset, limit) {
    console.log('get records');
    var defer = this.q.defer();
    this.connection.query("SELECT title, name, company FROM speaker LIMIT ? OFFSET ?", [limit, offset], function(error, rows, fields){
        console.log('get records done');
        data.rows = rows;
        defer.resolve(data);
        //defer.reject("earlier");
    });
    return defer.promise;
},

getTotal: function(data) {
    console.log('get total');
    var defer = this.q.defer();

    this.connection.query("SELECT * FROM speaker", function(error, rows, fields) { //SQL_CALC_FOUND_ROWS later
        data.total = rows.length;
        console.log('get total done');
        defer.resolve(data);
        //defer.reject("just like that");
    });

    return defer.promise;
},

onResult: function(data) {
    console.log('on result');
    puremvc.asyncproxy.AsyncProxy.prototype.onResult.call(this, data);
},

onFault: function(info) {
    puremvc.asyncproxy.AsyncProxy.prototype.onFault.call(this, info);
}

【问题讨论】:

  • 你有什么问题?
  • 请帮助获取 onResult(data),我得到的数据为 null
  • @AaronDufour 我已经编辑了问题
  • 这是使用 Promise 的糟糕方式。您的异步操作(例如getRecords())应该创建、返回和解决它们自己的promise,而不是对传入的defer 进行操作。然后您可以从.then() 处理程序返回该promise,以便按顺序链接操作。另外,给定的 defer/promise 只能使用一次,因此您不能对 getRecords()getTotal() 使用相同的 defer,这也是他们应该创建并返回自己的 promise 的原因。
  • @jfriend00 感谢您的批评,这就是我正在寻找的,在当前上下文中使用承诺的好方法,我想要的只是 data{} 被传递到链上,getRecords填充rows 字段,然后填充getTotal 填充total 字段,完成后调用onResult(data),您能否在使用最佳实践的同时重写上述内容。

标签: javascript node.js promise q


【解决方案1】:

这似乎可以简化。似乎您只想同步执行这些异步操作。如果你返回 Promise,链中的原始 Promise 将成为新的 Promise。最后的失败会捕获第一个被拒绝的承诺。

asyncAction: function(url, params, resultFunction, faultFunction) {
    puremvc.asyncproxy.AsyncProxy.prototype.asyncAction.call(this, resultFunction, faultFunction);

    if(!params.id) { //get master
        var queryString = this.url.parse(url, true).query;
        var offset = queryString.offset ? Number(queryString.offset) : 0;
        var limit = queryString.limit ? Number(queryString.limit) : 20;

        var data = {rows: null, total: null};

        var self = this;
        this.getRecords(data, offset, limit)
        .then(function(data) {
          return self.getTotal(data);
        })
        .then(this.onResult.bind(this))
        .fail(this.onFault);
    } else { //get detail
        this.getDetail(params.id);
    }
},

getRecords: function(data, offset, limit) {
    console.log('get records');
    var defer = this.q.defer();
    this.connection.query("SELECT title, name, company FROM speaker LIMIT ? OFFSET ?", [limit, offset], function(error, rows, fields){
        console.log('get records done');
        data.rows = rows;
        defer.resolve(data);
        //defer.reject("earlier");
    });
    return defer.promise;
},

getTotal: function(data) {
    console.log('get total');
    var defer = this.q.defer();

    this.connection.query("SELECT * FROM speaker", function(error, rows, fields) { //SQL_CALC_FOUND_ROWS later
        data.total = rows.length;
        console.log('get total done');
        defer.resolve(data);
        //defer.reject("just like that");
    });

    return defer.promise;
},

onResult: function(data) {
    console.log('on result');
    puremvc.asyncproxy.AsyncProxy.prototype.onResult.call(this, data);
},

【讨论】:

  • return self.onResult 令人困惑,onResult 没有返回任何内容,如果所有承诺都成功,它是一个独立的调用。 onResult 在成功时触发对最初调用 asyncAction 的原始调用者的回调。请根据需要修改答案
  • 问题,我也在某个地方看到过done,它有什么作用? .done(function(data){self.onResult('done ' + data)}),遇到错误怎么办
  • 你能看看我下面的答案吗,看看上面代码中的编辑 2 和我的 cmets onResult,你的方法很好,但如果你能给我一个 onResult cmets 的解决方案,我会接受你的回答。
  • 实际上你有答案,.then(this.onResult.bind(this)) 使用它有什么问题吗,因为它没有返回任何承诺,规格说明了什么
  • 我已经承诺getConnection,请您评论任何清理,有效的方法。我不得不恢复到基于闭包的方法,因为 resolve 只允许传递参数,所以我必须在 resolve 中创建一个对象,然后为下一个函数调用打开它,还必须为release 调用传递连接。如果您发现某些内容效率不高,请评论给定上下文中的最佳策略
【解决方案2】:

终于在苦苦挣扎之后,这是解决方案,但我需要更多的东西,在任何函数中调用 defer.reject 都不会停止进程,甚至执行完成

这篇文章很有帮助。 https://coderwall.com/p/ijy61g

编辑 2- 由于建议而使用绑定 - 问题是 onResult 被调用而不管失败,所以我必须做 if(data) 检查我不喜欢的,如果承诺有一些会很棒一种最终的.success.fail 的对应)函数。

编辑 3 - 在 then 链的末尾添加 this.onResult 尽管它没有返回任何承诺,它是否违反任何规范?

编辑 4 - 承诺getConnection

getConnection: function() {
    var defer = this.q.defer();

    var mysql = require("mysql");
    var pool = mysql.createPool({
        host: common.Config.mySQLHost,
        user: common.Config.mySQLUsername,
        password: common.Config.mySQLPassword,
        database: common.Config.mySQLDatabase
    });

    pool.getConnection(function(error, connection){
        if(error) { 
            defer.reject(error)
        } else {
            defer.resolve(connection);
        }
    });
    return defer.promise;
},

asyncAction: function(url, params, resultFunction, faultFunction) {

    if(!params.id) { //get master
        var queryString = this.url.parse(url, true).query;
        var offset = queryString.offset ? Number(queryString.offset) : 0;
        var limit = queryString.limit ? Number(queryString.limit) : 20;

        var data = {rows: null, total: null};

        var self = this;
        this.getConnection()
        .then(function(connection){return self.getRecords(data, offset, limit, connection)})
        .then(function(value){return self.getTotal(value.data, value.connection)})
        .then(function(value){self.onResult(value.data, value.connection)})
        .fail(function(value){self.onFault(value.error, value.connection)})
    } else { //get detail
        this.getDetail(params.id);
    }
},

getRecords: function(data, offset, limit, connection) {
    var defer = this.q.defer();
    connection.query("SELECT title, name, company FROM speaker LIMIT ? OFFSET ?", [limit, offset], function(error, rows, fields){
        if(error) {
            defer.reject({error:error, connection:connection});
        } else {
            data.rows = rows;
            defer.resolve({data: data, connection:connection});
        }
    });
    return defer.promise;
},

getTotal: function(data, connection) {
    var defer = this.q.defer();
    connection.query("SELECT count(*) AS total FROM speaker", function(error, rows, fields) { 
        if(error) {
            defer.reject({error:error, connection:connection});
        } else {
            data.total = rows[0].total;
            defer.resolve({connection:connection, data:data});
        }
    });
    return defer.promise;
},

onResult: function(data, connection) {
    console.log(data);
    connection.release();
},

onFault: function(info, connection) {
     console.log(info)
    connection.release();
}

【讨论】:

  • 你应该改为承诺connection.query,这会让你的生活更轻松。
  • asyncAction()中的promise链会不会简化为this.getRecords({}, offset, limit).then(this.getTotal).fail(this.onFault).done(this.onResult);datathat 可以消失。
  • @BenjaminGruenbaum 好提示 :)
  • @Roamer-1888 良好的上下文在 getRecords、getTotal、onFault 和 onResult 运行时很重要,我使用了闭包,但现在它被简化为 .call(this, param),同意吗?
  • 任何人都可以建议我如何在上述情况下进行错误处理,我想要的是调用 onFault 并且不调用 onResult,但现在即使承诺拒绝,两者都被调用
【解决方案3】:

User2727195,这是我的完整版本,基于您自己的答案中发布的未经编辑的原始代码。

没有尝试改进逻辑/流程,只是为了改进语法。因此,它仅与您当时的答案一样好。为了让一切正常运行,您需要应用您自己后来的想法和此后提供的其他建议。

因此,这不是一个独立的答案,不应选择。如果它开始吸引反对票,那么我会删除它,以便尽可能多地寻找想法。

asyncAction: function(url, params) {
    if(!params.id) { //get master
        var queryString = this.url.parse(url, true).query;
        this.getRecords({}, Number(queryString.offset || 0), Number(queryString.limit || 20))
            .then(this.getTotal.bind(this))
            .fail(this.onFault.bind(this))
            .done(this.onResult.bind(this));
        } else { //get detail
        this.getDetail(params.id);
    }
},
connectionQueryPromisifier = function() {
    // This is a promisifying adaptor for connection.query .
    // In your own version, you will probably choose to rewrite connection.query rather than use an adapter.
    var args = Array.prototype.slice.call(arguments).concat(function(error, rows, fields) {
        if(error) { defer.reject(error); }
        else { defer.resolve({ rows:rows, fields:fields }); }
    }),
        defer = this.q.defer();
    this.connection.query.apply(this, args);
    return defer.promise;
},
getRecords: function(data, offset, limit) {
    //Here, you take advantage of having promisified connection.query .
    return this.connectionQueryPromisifier("SELECT title, name, company FROM speaker LIMIT ? OFFSET ?", [limit, offset]).then(function(obj) {
        data.rows = obj.rows;
    });
},
getTotal: function(data) {
    //Here, you take advantage of having promisified connection.query .
    return this.connectionQueryPromisifier("SELECT * FROM speaker").then(function(obj) {
        data.total = obj.rows.length;
    });
},
onResult: function(data) {
    console.log('on result');
},
onFault: function(info) {
    console.log('onFault');
}

【讨论】:

  • 看起来很有趣,我会审查它
猜你喜欢
  • 2020-01-17
  • 1970-01-01
  • 1970-01-01
  • 2016-12-08
  • 1970-01-01
  • 1970-01-01
  • 2017-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多