可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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');
}