Break a Bluebird .each() process - bluebird

I'm converting from Async to Bluebird and can't figure out how to break a loop
Here's what I'm trying to achieve:
Loop through an array of data.
For each item, check if it exists on DB.
Add one item to the DB (first item that doesn't exist), and exit the .each() loop.
Any help will be highly appreciated.

Bluebird does not have a built in function for that type of operation and it's a little bit difficult to fit into the promise iteration model because iterators return a single value (a promise) which doesn't really give you the opportunity to communicate back both success/error and stop iteration.
Use Rejection to Stop Iteration
You could use Promise.each(), but you'd have to use a coded rejection in order to stop the iteration like this:
var data = [...];
Promise.each(data, function(item, index, length) {
return checkIfItemExists(item).then(function(exists) {
if (!exists) {
return addItemToDb(item).then(function() {
// successfully added item to DB
// lets reject now to stop the iteration
// but reject with a custom signature that can be discerned from an actual error
throw {code: "success", index: index};
});
}
})
}).then(function() {
// finished the iteration, but nothing was added to the DB
}, function(err) {
if (typeof err === "object" && err.code === "success") {
// success
} else {
// some sort of error here
}
});
This structure could be put into a reusable function/method if you have to use it regularly. You just have to adopt a convention for a rejected promise that really just meant to stop the iteration successfully rather than an actual error.
This does seem like an interesting and not all that uncommon need, but I haven't seen any particular defined structure with Promises for handling this type of issue.
If it feels like overloading a reject as in the above scenario is too much of a hack (which it sort of does), then you could write your own iteration method that uses a resolved value convention to tell the iterator when to stop:
Custom Iteration
Promise.eachStop = function(array, fn) {
var index = 0;
return new Promise(function(resolve, reject) {
function next() {
if (index < array.length) {
// chain next promise
fn(array[index], index, array.length).then(function(result) {
if (typeof result === "object" && result.stopIteration === true) {
// stopped after processing index item
resolve(index);
} else {
// do next iteration
++index;
next();
}
}, reject);
} else {
// finished iteration without stopping
resolve(null);
}
}
// start the iteration
next();
});
}
Here if the iterator resolves with a value that is an object has has a property stopIteration: true, then the iterator will stop.
The final promise will reject if there's an error anywhere and will resolve with a value of null if the iterator finished and never stopped or with a number that is the index where the iteration was stopped.
You would use that like this:
Promise.eachStop(data, function(item, index, length) {
return checkIfItemExists(item).then(function(exists) {
if (!exists) {
return addItemToDb(item).then(function() {
// return special coded object that has stopIteration: true
// to tell the iteration engine to stop
return {stopIteration: true};
});
}
})
}).then(function(result) {
if (result === null) {
// finished the iteration, but nothing was added to the DB
} else {
// added result item to the database and then stopped further processing
}
}, function(err) {
// error
});
Flag Variable That Tells Iterator Whether to Skip Its Work
In thinking about this some more, I came up with another way to do this by allowing the Promise.each() iteration to run to completion, but setting a higher scoped variable that tells your iterator when it should skip its work:
var data = [...];
// set property to indicate whether we're done or not
data.done = false;
Promise.each(data, function(item, index, length) {
if (!data.done) {
return checkIfItemExists(item).then(function(exists) {
if (!exists) {
return addItemToDb(item).then(function() {
data.done = true;
});
}
})
}
}).then(function() {
// finished
}, function(err) {
// error
});

Related

Angular - How to set up a parallel for loop in event binding function?

I have a function that expands every RowCol object in a FlexGridDetailProvider upon click. Right now, performance is bad with the way data binding works on FlexGrid rows, so I'm looking to improve performance by parallelizing each statement in the for loop.
Here's the typescript function:
if (thisDetailProvider!= null) {
setTimeout(() => {
try {
for (var t = 0; t < grid.rows.length; t++) {
if (thisDetailProvider.isDetailAvailable(t)) {
thisDetailProvider.showDetail(t);
this.gridSelectionService.clearSelectionFromGrids(thisDetailProvider.grid);
}
}
} catch (err) { console.log(err); }
}, 100);
}
I'd like the solution to be as simple as using the Parallel.For loop provided with C#. The solutions I've found so far require turning the event binding function into an asynchronous function, but I'd like to avoid introducing that complexity if there is a simpler way.
You can use async function to achieve the reduced code complexity. It is same as promise.
// `async` function | define a function start with `async` keyword
async myAsyncFunc() {
// #1 `async` ensures that the function returns a promise,
// even without explicitly return
return 123;
// #2 we can also `explicitly` return a promise
// this works same as above return
// return Promise.resolve(123);
// we can do both the ways but
// as `async` ensures that the function returns a promise
// so why to write extra code to return explicitly
}
// calling a function - and to get return result call then()
// the function inside then() will return the value
myAsyncFunc().then((returnVal) => {
console.log(returnVal); // 123
});
async yourFunction(){
for (var t = 0; t < grid.rows.length; t++) {
if (thisDetailProvider.isDetailAvailable(t)) {
thisDetailProvider.showDetail(t);
this.gridSelectionService.clearSelectionFromGrids(thisDetailProvider.grid);
}
}
}
In your case, I guess you can ignore the returning part which involves then

Async function returns two different results after another

I have written an async Flutter/Dart function which behaves unexpectedly in my opinion. Following code structure:
static Future<bool> verifySometing() async {
try {
await getCloudData().then((snapshot) {
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
});
} catch (e) {
print('Error $e');
return false;
}
print('Something went wrong');
return false;
}
The expected result would be that the function awaits the cloud data, then awaits validation and returns true if the data is valid. In this case, the console would show the following and the function would return true:
Data is correct
What happens in practice is that the console shows the following output and the function first returns true and then false:
Data is correct
Something went wrong
This goes against anything I thought to know about funtions in Dart because I always assumed that once return is fired, the function is done. Any ideas how this happens?
The issue is with this line.
await getCloudData().then((snapshot) {
Here, instead of just awaiting, you have also attached a then callback. So in actuality, whatever you are returning is return value of the callback function ie., (snapshot) {}.
The callback function that you need to pass into the then takes that return true and gives it to us as the result of await.
So, if you would've put something like
var bool = await getCloudData().then((snapshot) { ..... });
Then this bool, would've been equal to true. That's it. No return from the your main function.
Change it to this,
var snapshot = await getCloudData();
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
Hope, I was able to explain clearly.
There are a few of faults in your assumptions.
First, you've attached a then to the future returned by getCloudData(). This means that you aren't awaiting getCloudData(), but instead the additional future returned by getCloudData().then(...), and that future will return when the callback function passed to it completes. (And unless the first future throws an error, the callback will be called.)
Second, the callback function operates on its own scope. So this code is not doing what you think it's doing:
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
This return will affect the callback function, not the verifySomething function.
Given these, the order of operation is as follows:
The validateSomething function awaits getCloudData().then(...).
getCloudData() gets called.
getCloudData() returns, the callback passed to then is called.
(Assuming the snapshot has data) validateData is called.
(Assuming data is successfully validated) "Data is correct" gets printed and the callback function returns true.
validateSomething is notified that the awaited future is complete, so execution resumes.
"Something went wrong" gets printed and the validateSomething function returns false.
Generally speaking, these kinds of errors are common when mixing async/await and then patterns. Unless you know what you're doing, stick with either one or the other, preferably the async/await pattern. For example, a refactor of your code to eliminate the call to then is as follows:
static Future<bool> verifySometing() async {
try {
final snapshot = await getCloudData();
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
} catch (e) {
print('Error $e');
return false;
}
print('Something went wrong');
return false;
}
Now that there isn't a pesky closure to deal with, return will return from validateSomething as expected and you don't need to deal with issues like callbacks and scope.

cannot read the property of undefined

I am coding a server-side app in node JS and I am using a database in MySQL.
I get "TypeError: Cannot read property 'activated' of undefined"
The request I do should say "empty set" when I do it manually in the MySQL terminal.
When I try to use it in my code an I input and invalid discord_key, it returns an error, but I want it to return just a false alarm so I can catch it and use that info.
function checkKey(key) {
var activated = "";
var sqlcheck = "SELECT activated from authentification where discord_ key = ?";
console.log("in function");
DB.query(sqlcheck, [key], function (err, result) {
if (err) throw (err);
activated = result[0].activated;
});
if (!activated) {
console.log("null");
return ("NULL");
} else {
console.log("used");
return ("used");
}
}
I should get :
that request sends an empty set, so the key doesn't exist.
thank you for your help!
In case no result you can write this:
if (err) throw (err);
activated = result.length ? result[0].activated : false;
That will return false in case of no result.
The Error
The error is telling you that a variable you are using is undefined. It tells you this because you attempt to read a property from an undefined variable.
You mentioned result is an empty array. This means that any index you attempt to access returns undefined. For example:
let result = []
console.log(result[0] === undefined) // prints true
And in javascript, if you try and access a property of undefined, you get your error. Continuing our example:
result[0].activated // Throws error: Cannot read property 'activated' of undefined.
Since there is no guarentee that result[0] has a value, you should make sure it is not undefined before accessing it's properties. As #NipunChawla shows, one way is to check the array has a length (i.e at lease one value):
if (result.length) { // Does result have values?
activated = result[0].activated
} else {
activated = false
}
Better yet, if you know you are working with result[0] only, check whether it is defined directly:
if (result[0]) { // Does result[0] have a value?
activated = result[0].activated
} else {
activated = false
}
You are still left with the possibility that result[0].activated does not exist. Meaning activated would be undefined.
if (result[0] && result[0].activated) { // ... and does the first value
// contain the property activated?
activated = result[0].activated
} else {
activated = false
}
So all together now:
DB.query(sqlcheck, [key], function (err, result) {
if (err) throw (err);
if (result[0] && result[0].activated) {
activated = result[0].activated
} else {
activated = false
}
})
Async Callbacks
To fix !activated in the second if statement always being true, you should look into how callbacks work. Basically DB.query goes off and does its thing. When it is done, it will execute the function you provided it as a callback. The order of execution looks something like this:
Call DB.query to send a request to your database
Continue execution of your script. i.e check if (!activated) { ...
DB.query has now finished and calls your callback, assigning activated = result[0].activated. i.e function(err, result)
A quick way you could fix this would be like so:
function checkKey(key) {
var activated = "";
var sqlcheck = "SELECT activated from authentification where discord_ key = ?";
console.log("in function");
DB.query(sqlcheck, [key], function (err, result) {
if (err) throw (err);
if (result[0] && result[0].activated) {
activated = result[0].activated
} else {
activated = false
}
doSomethingWithResult(activated)
});
}
function doStuffWithResult(activated) {
if (!activated) {
console.log("null");
// Do your !activated stuff
} else {
console.log("used");
// Do your activated stuff
}
}
See this question for more info.

NodeJS return values mySQL

i have a question regarding nodeJS.
I have a LOOP in nodeJS that iterates mySQL table and depending on the result executes a specific query.
var query = "SELECT field1,fields2,field3 FROM database.table1";
mySQLconnection.query(query, function (err, data) {
if (err)throw err;
if (data.length != 0) {
for (var i = 0; i <= data.length - 1; i++) {
doFunction1(data[i].field1, data[i].field2, data[i].field3, function (func1data) {
if (func1data == false) {
doFunction2(data[i].field1, data[i].field2, data[i].field3, function (func2data) {
if (func2data == false) {
doFunction3(data[i].field1, data[i].field2, data[i].field3, function (func1data) {
});
}
});
}
});
}
}
});
The problem with the above approach is that it will not wait for the result from the first doFunction1, but rather it will continue with the i++
Either of your two functions doFunction1 or doFunction2 intiates some kind of asynchronous calls. Looks like you are not familiar with asynchronous behaviour of javascript. You can use the async.each to handle these type of situations.
for (var i=0; i<10000; i++) {
console.log(i);
some_async_function_call(i);
}
Basically this code initiates 10000 requests to execute your asyc functions. There's no guarantee your code will be executed before the for loop is finished.
What you need to do is to chain your async function calls so it will do things sequentially. There are multiple ways to achieve this. For example async.js or bluebird.js
async.js: series, waterfall, parallel... etc
https://github.com/caolan/async#waterfall
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
bluebird.js
https://github.com/petkaantonov/bluebird
var loop = function(){
// check loop conditions, return when the loop is finished
if (finished_loop) return Promise.resolve();
return Promise.promisify(your_function)()
.then(loop)
}
why do you want callbacks here in for loop. Write straight forward code which will be executed sequentially otherwise this code executes asynchronously.
At the end i went with a array and counter, when counter reaches the array length, it would restart it self. Basically recursive function.
Thanks for your help

jQuery - Deferreds waiting for an array of ajax requests to complete even failures

How can execute a function after a number of ajax requests have all completed regardless of whether they succeeded or error-ed out?
I've been trying to use $.when.apply(this, array) to pass an array of deferred jqXHR objects. However just like the docs say
In the multiple-Deferreds case where one of the Deferreds is rejected, jQuery.when immediately >fires the failCallbacks for its master Deferred. Note that some of the Deferreds may still be >unresolved at that point.
How can leverage jQuery deferred objects to always wait for all the ajax calls to finish?
Maybe I should create my own deferred that will wrap all the other deferreds? If so I'm not quite clear how to set that up.
In the spirit of how the Promise specification is likely going for the future with a PromiseInspection object, here's a jQuery add-on function that tells you when all promises are done, whether fulfilled or rejected:
(function() {
// pass either multiple promises as separate arguments or an array of promises
$.settle = function(p1) {
var args;
if (Array.isArray(p1)) {
args = p1;
} else {
args = Array.prototype.slice.call(arguments);
}
return $.when.apply($, args.map(function(p) {
// make sure p is a promise (it could be just a value)
p = wrapInPromise(p);
// Make sure that the returned promise here is always resolved with a PromiseInspection object, never rejected
return p.then(function(val) {
return new PromiseInspection(true, val);
}, function(reason) {
// Convert rejected promise into resolved promise by returning a resolved promised
// One could just return the promiseInspection object directly if jQuery was
// Promise spec compliant, but jQuery 1.x and 2.x are not so we have to take this extra step
return wrapInPromise(new PromiseInspection(false, reason));
});
})).then(function() {
// return an array of results which is just more convenient to work with
// than the separate arguments that $.when() would normally return
return Array.prototype.slice.call(arguments);
});
}
// utility functions and objects
function isPromise(p) {
return p && (typeof p === "object" || typeof p === "function") && typeof p.then === "function";
}
function wrapInPromise(p) {
if (!isPromise(p)) {
p = $.Deferred().resolve(p);
}
return p;
}
function PromiseInspection(fulfilled, val) {
return {
isFulfilled: function() {
return fulfilled;
}, isRejected: function() {
return !fulfilled;
}, isPending: function() {
// PromiseInspection objects created here are never pending
return false;
}, value: function() {
if (!fulfilled) {
throw new Error("Can't call .value() on a promise that is not fulfilled");
}
return val;
}, reason: function() {
if (fulfilled) {
throw new Error("Can't call .reason() on a promise that is fulfilled");
}
return val;
}
};
}
})();
Then, you can use it like this:
$.settle(promiseArray).then(function(inspectionArray) {
inspectionArray.forEach(function(pi) {
if (pi.isFulfilled()) {
// pi.value() is the value of the fulfilled promise
} else {
// pi.reason() is the reason for the rejection
}
});
});
Keep in mind that $.settle() will always fulfill (never reject) and the fulfilled value is an array of PromiseInspection objects and you can interrogate each one to see if it was fulfilled or rejected and then fetch the corresponding value or reason. See the demo below for example usage:
Working demo: https://jsfiddle.net/jfriend00/y0gjs31r/