Sequencing async operations followed by onResult call

|▌冷眼眸甩不掉的悲伤 提交于 2020-01-07 09:26:23

问题


Fiddle: http://jsfiddle.net/smartdev101/eLxxpjp3/

Inside asyncAction function call, a promise has been created to sequence two async operations, getRecords and getTotal followed by a final call to onResult(data) on success or onFail(info) in case of exception.

how to get onResult call upon completion of two async operations?

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

回答1:


It seems like this could be simplified. It seems like you just want these async operations to be performed synchronously. If you return the promise, the original promise in the chain will become the new promise. The final fail will catch the first promise to be rejected.

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



回答2:


finally after struggling a lot, here's the solution, but I need couple of more things, calling defer.reject in any function doesn't halt the process, even done is executed

this post helped a lot. https://coderwall.com/p/ijy61g

Edit 2- Used bind as a result of suggestions - the problem is onResult gets called irrespective of failure, so I had to do if(data) check which I don't like, would have been awesome if promises had some kind of final .success (counterpart of .fail) function.

Edit 3 - added this.onResult at the end of then chain though it doesn't return any promise, is it violating any specs?

Edit 4 - promisified 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();
}



回答3:


User2727195, here's my version in full, based on the original, unedited code posted in your own answer.

There's no attemtpt to improve the logic/flow, just to improve the syntax. As such, it is only as good as your answer was at that time. To get everything working properly, you will need to apply your own later ideas and other suggestions offered since then.

As such, this is not an independent answer and should not be selected. If it starts to attract negative votes, then I will delete it so trawl through for ideas while you can.

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


来源:https://stackoverflow.com/questions/26553098/sequencing-async-operations-followed-by-onresult-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!