Jquery Deferred in each loop with ajax callback - jquery-deferred

With reference to https://stackoverflow.com/a/13951699/929894, I tried using deferred object in nested ajax loop. However the output is not returning as expected. I have updated my code in fiddle for reference. - https://jsfiddle.net/fewtalks/nvbp26sx/1/.
CODE:
function main() {
var def = $.Deferred();
var requests = [];
for (var i = 0; i < 2; i++) {
requests.push(test(i));
}
$.when.apply($, requests).then(function() {
def.resolve();
});
return def.promise();
}
function test(x){
var def = $.Deferred();
test1(x).done(function(){
setTimeout(function(){ console.log('processed test item', x); def.resolve();}, 1000);
});
return def.promise();
}
function test1(items){
var _d = $.Deferred();
setTimeout(function(){
console.log('processed test1 item', items);
_d.resolve();
});
return _d.promise();
}
main().done(function(){ console.log('completed')});
Code contains a main function which executes loop. On each loop, a sub function(test) is executed. Inside the sub function(test) another function(test1) is called. Both sub functions test and test1 has AJAX call declaration. For AJAX call I have used setTimeout property. I'm expecting an output like
processed test1 item0
processed test item0
processed test1 item1
processed test item0
completed
For each loop, I want the function to be executed as Test1() then test(); However I'm getting the result as
processed test1 item 0
processed test1 item 1
processed test item 0
processed test item 1
completed
After executing the test1 completely test function is executed. Why the function is not executing sequentially for each loop.
UPdated code for another test run
function main(items) {
var items = items;
return items.reduce(function (p, index) {
return p.then(function () {
return test(index);
});
}, $.Deferred().resolve());
}
function test(x) {
var def = $.Deferred();
test1(x).done(function () {
setTimeout(function () {
log('processed test item', x);
def.resolve();
}, 1000);
});
return def.promise();
}
function test1(items) {
var _d = $.Deferred();
setTimeout(function () {
log('processed test1 item', items);
_d.resolve();
});
return _d.promise();
}
var items = [0, 1];
function test2(x) {
var _d = $.Deferred();
setTimeout(function () {
log('processed test2 item',x);
_d.resolve();
});
return _d.promise();
}
main([1,2]).done(function(data){test2(items);}).then(function () {
log('completed')
});
<script src="https://dl.dropboxusercontent.com/u/7909102/log.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
why 'completed' is logged before processing test2 function?

Your result is as expected.
Your for loop runs synchronously to completion and runs test() twice.
test() then immediately calls test1() so the first thing you see is that test1() gets to run twice. Then, after each test1() completes its promise, it sets the timer for your test() log message. So naturally, the two log messages from test() comes after the two log messages from test1().
Remember that $.when() runs things in parallel so all the promises you pass it are in flight at the same time.
If you want to serialize your calls to test(i) so the next one doesn't happen until after the first one, then you need to do things differently.
Also, you are using an anti-pattern in main() by creating a deferred where you don't need to create one. You can just return $.when.apply(...). You don't need to wrap it in another deferred.
To serialize your calls to test(i) to get the type of output you indicate you wanted, you can do this:
function main() {
var items = [0, 1];
return items.reduce(function(p, index) {
return p.then(function() {
return test(index);
});
}, $.Deferred().resolve());
}
Working demo that generates your desired output: https://jsfiddle.net/jfriend00/hfjvjdcL/
This .reduce() design pattern is frequently used to serially iterate through an array, calling some async operation and waiting for it to complete before calling the next async operation on the next item in the array. It is a natural to use .reduce() because we're carrying one value through to the next iteration (a promise) that we chain the next iteration to. It also returns a promise too so you can know when the whole thing is done.

Related

JSON array undefined & empty in promise Bluebird

I am using Promise bluebird to process a json array objects from file. The problem arises if I want to store data in a json array (called list) and return this in the final process.
The list is empty/undefined after the return of list or even in the final process. Running the code, I always have 1 value that is not false which trigger the adding/push of the json in the list.
Can you help me with this issue? Below you will find my code.
Thanks in advance !!!
var Promise = require('bluebird');
var join = Promise.join;
var fs = Promise.promisifyAll(require("fs"));
fs.readdirAsync(dir).map(function (filename) {
return fs.readFileAsync(dir + "/" + filename, "utf8");
}).then(function(result){
var list=[];
result.map(function(row, index){
Promise.coroutine(function*() {
update(row, index).then(function(value){
if (value!=false){
var trade_update = new updated_Item(row.ID, row.Quantity, row.Price, row.Remark);
list.push(trade_update);
console.log(JSON.stringify(list)); <-- This works. It gives me data
}
return list;
})
})();
});
console.log('list: ' + JSON.stringify(list)); <-- output: list:[]
return list;
}).finally(function(result){
console.log('Final outcome: '+ ' ' + JSON.stringify(result)); <-- output: Final outcome: undefined
})
With the help of Samuel my code is now:
var Promise = require('bluebird');
var join = Promise.join;
var fs = Promise.promisifyAll(require("fs"));
function updateOrder(done){
fs.readdirAsync(dir).map(function (filename) {
return fs.readFileAsync(dir + "/" + filename, "utf8");
}).then(function(result){
var list=[];
result.map(function(row, index){
Promise.coroutine(function*() {
update(row, index).then(function(value){
if (value!=false){
var trade_update = new updated_Item(row.ID, row.Quantity, row.Price, row.Remark);
list.push(trade_update);
done(list);
}
})
})();
});
//done(list); <--if I put the done callback here, it will give me an empty list. I though once the result.map finished processing all the values give me the end result.
}
}
updateOrder(function(resultList){
console.log('List' + JSON.stringify(resultList));
})
This code give me whole resultList everytime the list has been updated (pushed) now.
I would to receive the resultList at the end once the function updateOrder is finished.
As noted in the comment. Promise.coroutine is asynchronous so this means that a result is not going to get return straight after your code reaches it. And this pretty much explains the phenomenon you are seeing where the latter print statements you got in the code is suggesting that list is undefined.
What you could do is wrap the entire code you got there in a function, then add a callback function as a parameter for the async functions to invoke when it has finished its duty, together returning the populated list back for later processing.
I have written a pseudo code for your case, unfortunately I couldn't test it on my IDE but the concept is there and it should work.
Consider my pseudo code:
var Promise = require('bluebird');
var join = Promise.join;
var fs = Promise.promisifyAll(require("fs"));
// Wrap everything you got into a function with a `done` parameter (callback fn)
function doStuff(done) {
fs.readdirAsync(dir).map(function (filename) {
return fs.readFileAsync(dir + "/" + filename, "utf8");
}).then(function(result){
var list=[];
result.map(function(row, index){
Promise.coroutine(function*() {
update(row, index).then(function(value){
if (value!=false){
var trade_update = new updated_Item(row.ID, row.Quantity, row.Price, row.Remark);
list.push(trade_update);
}
done(list);
})
})();
});
}).finally(function(result){
console.log('File read finish, but this doesnt mean I have finished doing everything!');
})
}
// call your function and provide a callback function for the async method to call
doStuff(function(resultList) {
console.log('list: ' + JSON.stringify(resultList));
// Continue processing the list data.
});

Trouble with method that takes in a function and its arguments

I am having trouble passing a kata. I believe I am on the right track, but do not fully understand how to retrieve the desired results.
The Instructions
Write a method that takes in a function and the arguments to the function and returns another function which when invoked, returns the result of the original function invoked with the supplied arguments.
Example Given
Given a function add
function add (a, b) {
return a + b;
}
One could make it lazy as:
var lazy_value = make_lazy(add, 2, 3);
The expression does not get evaluated at the moment, but only when you invoke lazy_value as:
lazy_value() => 5
Here is my half a day endeavor conclusion
var make_lazy = function () {
var innerFunction = null;
var array = [];
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == 'function') {
innerFunction = arguments[i];
} else {
array.push(arguments[i]);
}
}
innerFunction.apply(innerFunction, array);
innerFunction();
};
I'm using arguments and apply() and think I am close? However I am getting TypeError: lazy_sum is not a function at Object.exports.runInThisContext within test results. Any help, especially understanding what is going on, is appreciated. Thanks
...
return function() {
return innerFunction.apply(this, array);
};
};
Thanks again all. Problem solved.

nightwatch.js return value from function outside a test

I have trouble moving certain code outside a test into a function that needs to return a value.
Here is part of my code for the test file
function getCountOfTopics(browser){
var count;
browser.getText('#sumTopics',
function(result){
count = result.value;
console.log(result.value);
}
);
return count;
};
module.exports = {
'Create article' : function(browser){
var noOfThreadsByInlineCode, noOfThreadsByFunction;
browser.getText('#sumTopics',
function(result){
noOfThreadsByInlineCode = result.value;
}
);
noOfThreadsByFunction = getCountOfTopics(browser);
browser.end();
}
}
Now, the variable noOfThreadsByInlineCode indeed gets the value in the DOM, but the variable noOfThreadsByFunction is undefined. The console does indeed print the correct value, so the function does get the correct value out of the DOM.
I would appreciate help in updating the function so that I do get the value returned.
One word answer is Asynchronisity. The code doesn't wait for your callback to get complete, thats what the feature of Node JS is.
If you are in desperately in need for the content inside of the callback you can write this variable into a file and then access it anywhere you want inside your code. Here's a bit of a workaround:
Save something in a file:
var fs = require('fs');
iThrowACallBack(function(response){
fs.writeFile('youCanSaveData.txt', this.response, function(err) {
if (err) throw err;
console.log('Saved!');
browser.pause(5000);
});
});
Access it somewhere else:
iAccessThefile(){
response = fs.readFileSync('youCanSaveData.txt').toString('utf-8');
}
Hope it helps.
You return variable 'count' outside the callback,that is why.You can take a look this topic How to return value from an asynchronous callback function?
function getCountOfTopics(browser){
var count;
browser.getText('#sumTopics',
function(result){
count = result.value;
console.log(result.value);
/// result.value is available in this callback.
}
);
What do you want to do with the 'value'?
ps:do not remember custom_command.I think it is very helpful for this issue.

NodeJS calling function twice generates error

I have a for loop that calls the following function twice:
var getJSON = function (url, callback) {
var http = require('https');
http.get(url, function (res) {
var body = '';
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
//console.log("Got response: ", body);
var response = JSON.parse(body);
return callback(response);
});
}).on('error', function (e) {
console.log("Got error: ", e);
return callback(-1);
});
}
The url parameter is at first "https://api.bitok.com/open_api/btc_eur/ticker" and the second time "https://api.bitok.com/open_api/btc_usd/ticker". The callback parameter is just another function that the program should go. The problem is that if it only works the first time (no matter which of both endpoints), the second time is falling and not printing the error, not sure what to do.
EDIT
Here is the for loop where I call the function, I also can't understand what is wrong, "exch.pairs_list.length" is equal to 2.
for (var i = 0; i < exch.pairs_list.length; i++) {
getJSON(url, callback);
}
No error is been thrown, the problem is the callback function, it's only been called once, should be twice.
I'm sorry guys, the mistake was very silly, I was trying to print the result before I actually receive response from the API.

How to loop through indexedDB tables synchronously?

I want to write a function in JS where I will loop through a tables in my indexed DB and get the maximum value of last modified of table and return that
function readData(){
var trans = '';
trans = idb.transaction(["tableName"],'readonly'); // Create the transaction
var request = trans.objectStore("tableName").openCursor();
request.onsuccess = function(e) {
var cursor = request.result || e.result;
if(cursor) {
// logic to and find maximum
} else {
return // max last modified
}
cursor.continue();
}
}
IMP--Since onsuccess method is asynchronous how can i make it synchronous? so that my method readData() will return only when max last modified record is found successfully. I can call this method(readData()) synchronously to get last modified record of 2-3 tables if I want.
The sync API is only available in a webworker. So this would be the first requirement. (As far as I know only IE10 supports this at the moment)
An other shot you can give is working with JS 1.7 and use the yield keyword. For more information about it look here
I would sugest to work with a callbakck method that you call when you reached the latest value.
function readData(callback){
var trans = '';
trans = idb.transaction(["tableName"],'readonly'); //Create the transaction
var request = trans.objectStore("tableName").openCursor();
var maxKey;
request.onsuccess = function(e) {
var cursor = request.result || e.result;
if(cursor.value){
//logic to and find maximum
maxKey = cursor.primaryKey
cursor.continue();
}
}
trans.oncomplete = function(e) {
callback(maxKey);
}
}
IndexedDB API in top frame is async. async cannot be synchronous. But you can read all tables in single transaction.