Say I have a function as follows:
var randomFn = function(){
var response = '';
async.whilst(
// Test
function(){
return i < 5;
},
// Iteratee
function(callback){
// Random code that sets response to '5' after some attempts;
},
// Callback
function(err, n){
// Runs if error is encountered or iteratee stopped
}
);
return response;
}
The async module while allow the iteratee function to run repeatedly, until the first test function no longer returns true. My question is, can I be guaranteed the return for my randomFn() will only be executed once the everything inside the async function has completed?
So what exactly is the order of execution, or the rule for execution...?
Related
I want to block any other execution until evaluateTestCases function finishes but before the function finishes, It goes in other function that is then of same function.
Here is the function:
function evaluateTestCases(source, lang)
{
return new Promise(function(resolve)
{
for(let i=0;i<hiddenData.testCases.length;i++)
{
getOutput(source, lang, hiddenData.testCases[i].input).then(function(response){
console.log(hiddenData.testCases[i].output.toString().trim()===response.output.toString().trim());
if(hiddenData.testCases[i].output.toString().trim()===response.output.toString().trim())
{
points+=1;
}
});
}
resolve(points);e
});
}
Here is the function call:
$("#submit").on("click",function()
{
evaluateTestCases(editor.getValue(), $('#language').val()).then(function(result){
console.log(result);
});
});
Now, what happens is: before evaluateTestCases function finishes off, console.log(result); gets executed and since result is not yet defined, it acts as an error for me.
You are creating promises in the loop, but you are not waiting for them to be settled.
Either use Promise.all (if multiple getOutput calls may be triggered before they signal completion), or use await getOutput(...). The latter solution will wait for each returned promise to be settled before continuing into the next iteration of the loop. You need to make the outer function async:
async function evaluateTestCases(source, lang) {
let points = 0; // Not sure where you were initialising this...
for (let i = 0; i < hiddenData.testCases.length; i++) {
const response = await getOutput(source, lang, hiddenData.testCases[i].input);
console.log(hiddenData.testCases[i].output.toString().trim() === response.output.toString().trim());
if (hiddenData.testCases[i].output.toString().trim() === response.output.toString().trim()) {
points+=1;
}
});
return points;
}
The main function call can remain like you had it.
Debugging in this example code, and pressing f10(step over) each time, debugger will enter into a file name next_tick.js which is a file related to Node.js. Why? I do not want to step into internal code related to Node.js.
Below is example code
function abc() {
return new Promise((resolve, reject) => {
setTimeout(function () { resolve(10) }, 1000)
});
}
async function b(){
const a=10+ await abc()
console.log(a)
}
b()
Below is part of next_tick.js
// Run callbacks that have no domain.
// Using domains will cause this to be overridden.
function _tickCallback() {
do {
while (tickInfo[kIndex] < tickInfo[kLength]) {
const tock = nextTickQueue[tickInfo[kIndex]++];
const callback = tock.callback;
const args = tock.args;
// CHECK(Number.isSafeInteger(tock[async_id_symbol]))
// CHECK(tock[async_id_symbol] > 0)
// CHECK(Number.isSafeInteger(tock[trigger_id_symbol]))
// CHECK(tock[trigger_id_symbol] > 0)
nextTickEmitBefore(tock[async_id_symbol], tock[trigger_id_symbol]);
// emitDestroy() places the async_id_symbol into an asynchronous queue
// that calls the destroy callback in the future. It's called before
// calling tock.callback so destroy will be called even if the callback
// throws an exception that is handles by 'uncaughtException' or a
// domain.
// TODO(trevnorris): This is a bit of a hack. It relies on the fact
// that nextTick() doesn't allow the event loop to proceed, but if
// any async hooks are enabled during the callback's execution then
// this tock's after hook will be called, but not its destroy hook.
if (async_hook_fields[kDestroy] > 0)
emitDestroy(tock[async_id_symbol]);
// Using separate callback execution functions allows direct
// callback invocation with small numbers of arguments to avoid the
// performance hit associated with using `fn.apply()`
_combinedTickCallback(args, callback);
nextTickEmitAfter(tock[async_id_symbol]);
if (kMaxCallbacksPerLoop < tickInfo[kIndex])
tickDone();
}
tickDone();
_runMicrotasks();
emitPendingUnhandledRejections();
} while (tickInfo[kLength] !== 0);
}
The documentation for the _.throttle function states that:
Creates a throttled function that only invokes func at most once per
every wait milliseconds. The throttled function comes with a cancel
method to cancel delayed func invocations and a flush method to
immediately invoke them. Provide an options object to indicate whether
func should be invoked on the leading and/or trailing edge of the wait
timeout. The func is invoked with the last arguments provided to the
throttled function. Subsequent calls to the throttled function return
the result of the last func invocation
I'm interested in this line:
Subsequent calls to the throttled function return
the result of the last func invocation
I've tried:
var throttled = _.throttle(updateModelData, 1000);
service.on('change', function () {
throttled(5);
});
function updateModelData(data) {
// all calls here log 5's
console.log(data);
return data;
}
setTimeout(function() {
throttled(); // here updateModelData is executed with `undefined` value
}, 5000);
The problem is that throttled() triggers function without returning the data. How can I invoke it so that it returns last data?
EDIT:
According to source code, the value will be returned only if no pending function call exists isCalled === false:
function debounced() {
args = arguments;
stamp = now();
thisArg = this;
trailingCall = trailing && (timeoutId || !leading);
if (maxWait === false) {
var leadingCall = leading && !timeoutId;
} else {
if (!maxTimeoutId && !leading) {
lastCalled = stamp;
}
var remaining = maxWait - (stamp - lastCalled),
isCalled = remaining <= 0 || remaining > maxWait;
!!!!! HERE
if (isCalled) {
if (maxTimeoutId) {
maxTimeoutId = clearTimeout(maxTimeoutId);
}
lastCalled = stamp;
result = func.apply(thisArg, args);
}
else if (!maxTimeoutId) {
maxTimeoutId = setTimeout(maxDelayed, remaining);
}
}
...
return result;
}
So the following will work:
var throttled = _.throttle(updateModelData, 10000);
service.on('change', function () {
throttled(5);
});
function updateModelData(data) {
// all calls here log 5's
console.log(data);
return data;
}
setTimeout(function() {
throttled(); // returns 5
}, 15000);
The issue is that, when you have leading invocations (the default behavior for _.throttle), when you first call the throttled function (or first call it after after your delay time has passed) it immediately calls the underlying function, before returning anything.
That means that the "result of the last function invocation" might be the result of a function invocation that was caused by your current call to the throttled function. So your call to throttle() calls updateModelData() and then returns undefined, since updateModelData() returns undefined.
Here's some sample code that might clarify this:
var foo = (x) => x;
var leading = _.throttle(foo, DELAY, {leading: true, trailing: false}); //these are the default options for leading and trailing
var trailing = _.throttle(foo, DELAY, {leading: false, trailing: true});
leading(1); //Calls foo(1), returns 1
leading(2); //Doesn't call foo, returns 1,
leading(3); //Doesn't call foo, returns 1
trailing(1); //Doesn't call foo, returns undefined
trailing(2); //Doesn't call foo, returns undefined
//DELAY ms later
//foo(2) is called, due to the most recent call to bar2
leading(); //Calls foo(), returns undefined
leading(1); //Still returns undefined from above
trailing(); //Doesn't call foo, returns 2
trailing(1); //Doesn't call foo, returns 2
//Another DELAY ms later
leading("Whatever"); //Calls foo("Whatever"), returns "Whatever";
Here's a version of your JSFiddle that makes it slightly more obvious too.
Really, you shouldn't call a function just to get the last value returned by it, so I'd suggest you just manage the last value yourself and not rely on _.throttle to do it for you. For example:
var lastResultOfFoo;
var foo = function (x) {
lastResultOfFoo = x;
return x;
}
//OR (if you don't mind attaching arbitrary properties to functions)
var foo = function (x) {
foo.lastResult = x;
return x;
}
The following code works fine:
var throttled = _.throttle(updateModelData, 1000);
var i = 0;
function updateModelData(data) {
return data;
}
var interval = setInterval(function() {
console.log(throttled(i++));
if (i === 6) {
clearInterval(interval);
console.log('Last value: ' + throttled());
}
}, 2000);
Output:
0
1
2
3
4
5
"Last value: 5"
DEMO
I want to run func2 run only after func1 has finished.
{
func1();
func2();//
}
But when func1() starts, func2() does not wait for func1() to get finished. func2() run simultaneously with func1(), which causes run time error, because func2() need some input from func1().
I also tried using async module, but got no success.
Edit: I run the function check(), which call two functions prompt() and copyProject(). copyProject() should run only after prompt() function has finished, but it start executing simultaneously.
var check = function check (){
async.series([
function(callback){
prompt();
callback(null);
},
function(callback){
copyProject();
callback(null);
}
]);
};
var prompt = function prompt(){
var prompts = [{
name: "projectName",
message: "What is the name of your project?"
},{
name: "authorName",
message: "What is your name?",
}];
inquirer.prompt(prompts, function( answers ) {
for (var key in answers) {
if (answers.hasOwnProperty(key)) {
console.log(key + " -> " + answers[key]);
infoToRender[key] = answers[key]
}
}
});
};
var copyProject = function copyProject (){
// code to copy some files from one location to another based on input from prompt function
};
Your function prompt is itself an asynchronous function: The call to inquirer.prompts causes the function to wait and this makes it asynchronous. Since your prompt function does not use a callback, the async module can not know when prompt has finished: In contrast, it runs prompt and this function immediately returns. Hence async right afterwards runs copyProject.
What you need to do to solve this issue is to introduce a callback function in prompt that async "waits" for. So basically something like this:
var prompt = function (callback) {
// Do your stuff here...
inquirer.prompt(prompts, function (answers) {
// Your for loop here...
callback(null);
});
};
Then, within async you need to do:
async.series([
function (callback) {
prompt(callback);
},
function (callback) {
copyProject();
callback(null);
}
]);
Then copyProject is only run when prompt has been completed. Please note that async.series will not wait for copyProject to finish, in case this is an asynchronous function, as again, it does not take a callback. So same pattern applies here.
I'm trying to work through this js/async scenario and i'm trying to know how the rest of the js world handles this.
function doStuff(callback) {
cursor.each(function(err, blahblah) {
...doing stuff here takes some time
});
... Execute this code ONLY after the `cursor.each` loop is finished
callback();
EDIT
Here's a more concrete example updated using most of the suggestions below which still doesn't work.
function doStuff(callback) {
MongoClient.connect(constants.mongoUrl, function(err, db) {
var collection = db.collection('cases2');
var cursor = collection.find();
var promises = []; // array for storing promises
cursor.each(function(err, item) {
console.log('inside each'); // NEVER GETS LOGGED UNLESS I COMMENT OUT THIS LINE: return Q.all(promises).then(callback(null, items));
var def = Q.defer(); // Create deferred object and store
promises.push(def.promise); // Its promise in the array
if(item == null) {
return def.resolve();
}
def.resolve(); // resolve the promise
});
console.log('items'); // ALWAYS GETS CALLED
console.log(items);
// IF I COMMENT THIS LINE OUT COMPLETELY,
// THE LOG STATEMENT INSIDE CURSOR.EACH ACTUALLY GETS LOGGED
return Q.all(promises).then(callback(null, items));
});
}
without using promises or any other dependencies/libraries you can simply
function doStuff(callback) {
add a counter
var cursor = new Array(); // init with some array data
var cursorTasks = cursor.length;
function cursorTaskComplete()
{
cursorTasks--;
if ( cursorTasks <= 0 ) {
// this gets get called after each task reported to be complete
callback();
}
}
for ( var i = 0; i < cursor.length; i++ ) {
...doing stuff here takes some time and does some async stuff
check after each async request
...when async operation is complete call
cursorTaskComplete()
}
}
Without knowing the details of the async calls you're making within the cursor.each loop, I shall assume that you have the ability to invoke a callback each time the functions invoked therein have completed their async task:
function doStuff() {
var promises = []; // array for storing promises
cursor.each(function(err, blahblah) {
var def = Q.defer(); // create deferred object and store
promises.push(def.promise); // its promise in the array
call_async_function(..., def.resolve); // resolve the promise in the async function's callback
});
// pass the array to Q.all, only when all are resolved will "callback" be called
return Q.all(promises);
}
and the usage then becomes:
doStuff().then(callback)
Note how the invocation of the callback now never touches the doStuff function - that function now also returns a promise. You can now register multiple callbacks, failure callbacks, etc, all without modifying doStuff. This is called "separation of concerns".
[NB: all the above based on the Q promises library - https://github.com/kriskowal/q]
EDIT further discussion and experimentation has determined that the .each call is itself async, and gives no indication to the outside when the last row has been seen. I've created a Gist that demonstrates a resolution to this problem.
if you want to do it with the async module, you can make use of the async forEachSeries function
Code snippet:
function doStuff(callback) {
async.forEachSeries(cursor, function(cursorSingleObj,callbackFromForEach){
//...do stuff which takes time
//this callback is to tell when everything gets over execute the next function
callbackFromForEach();
},function(){
//over here the execution of forEach gets over and then the main callback is called
callback();
});
}
In my mind an elegant/ideal solution would be to have something like
cursor.each(........).then( function() { ....your stuff});
But without that you can do this....UPDATED
http://plnkr.co/edit/27l7t5VLszBIW9eFW4Ip?p=preview
The gist of this is as shown below...notice....when
var doStuff = function(callback) {
cursor.forEach(function(cursorStep) {
var deferred = $q.defer();
var promise = deferred.promise;
allMyAsyncPromises.push(promise);
cursorStep.execFn(cursorStep.stepMeta);
promise.resolve;
});
$q.when(allMyAsyncPromises).then(callback);
}
After hitting the start button wait for few seconds...the async tasks have been simulated to finish in 5 seconds so the status will update accordingly.
Not having access to a real cursor object..I had to resort of fake cursor like and array.