-2

Maybe this is a general issue, and i need a solution to my case : due to the non blocking aspect of javascript , I don't find how can I execute my function with all iteration in for loop , and here is my example ,

var text_list=[]
for (var i = 0; i < 10; i++) {
    var element = array[index];
    tesseract.process("img"+i+".jpg", options, function (err, text) {
        if (err) {
            return console.log("An error occured: ", err);
        }
        text_list.push(text)
    });
}
console.log(text_list) //

And the result as if I do :

tesseract.process("img"+9+".jpg"...
tesseract.process("img"+9+".jpg"...
tesseract.process("img"+9+".jpg"...
.
.
.

and what i need is :

tesseract.process("img"+0+".jpg"...
tesseract.process("img"+1+".jpg"...
tesseract.process("img"+2+".jpg"...
.
.
. 
nadhem
  • 178
  • 4
  • 20
  • 1
    I'm sorry; maybe I'm just not understanding, but can revise your question to better clarify what you're hoping to achieve? – Trent Jul 30 '17 at 00:04
  • It's done , I add what i want to achieve @Trent – nadhem Jul 30 '17 at 00:13
  • 2
    Is there code you're not showing? Because this looks like if shouldn't be causing any trouble and you have some things that don't make sense like 'array[index]' (what's index?) – Mark Jul 30 '17 at 00:19
  • Possible duplicate of [Javascript infamous Loop issue?](https://stackoverflow.com/questions/1451009/javascript-infamous-loop-issue) – James Jul 30 '17 at 00:22

1 Answers1

2

Your question does not really explain what result you are getting and your code looks like it's missing parts of the code. So, all I can really do here to help is to explain generically (using your code where possible) how to solve this class of problem.

If you are ending up with a lot of results that all reference the last value of i in your loop, then you are probably trying to reference i in an async callback but because the callback is called sometime later, the for loop has already finished long before the callback executes. Thus, your value of i is sitting on the last value it would have in the for loop. But, your question doesn't actually show code that does that, so this is just a guess based on the limited result you describe. To solve that type of issue, you have make sure you're separately keeping track of i for each iteration of the loop. There are many ways to do that. In ES6, using let in the for loop definition will solve that entire issue for you. One can also construct a closure, use .forEach(), etc...

Async operations with a loop require extra work and coding to deal with. The modern solution is to convert your async operations to use promises and then use features such as Promise.all() to both tell you when all the async operations are done and to keep the results in order for you.

You can also code it manually without promises. Here's a manual version:

const len = 10;
let text_list = new Array(10);
let doneCnt = 0;
let errFlag = false;

// using let here so each invocation of the loop gets its own value of i
for (let i = 0; i < len; i++) {
    tesseract.process("img"+i+".jpg", options, function (err, text) {
        if (err) {
            console.log("An error occured: ", err);
            // make sure err is wrapped in error object
            // so you can tell errors in text_list array from values
            if (!(err instanceof Error)) {
                err = new Error(err);
            }
            text_list[i] = err;
            errFlag = true;
        } else {
            text_list[i] = text;
        }
        // see if we're done with all the requests
        if (++doneCnt === len) {
            if (errFlag) {
                // deal with situation where there were some errors
            } else {
                // put code here to process finished text_list array
            }
        }
    });
}
// you can't process results here because async operations are not
// done yet when code here runs

Or, using promises, you can make a "promisified" version of tesseract.process() and then use promise functionality to track multiple async operations:

// make promisified version of tesseract.process()
tesseract.processP = function(img, options) {
    return new Promise(function(resolve, reject) {
        tesseract.process(img, options, function(err, text) {
            if (err) {
                reject(err)
            } else {
                resolve(text);
            }
        });
    });
}

const len = 10;
let promises = [];
for (let i = 0; i < len; i++) {
    promises.push(tesseract.processP("img"+i+".jpg", options));
}
Promise.all(promises).then(function(results) {
    // process results array here (in order)
}).catch(function(err) {
    // handle error here
});
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Shouldn't the variable in the argument to `tesseract.process` be interpolated correctly regardless of `var` or `let` - it's not asynchronous here, is it? – Mark Jul 30 '17 at 00:22
  • @Mark_M - That isn't where an issue with `i` would be. The issue is where I use it to assign `text_list[i] = xxx` in the async callback. – jfriend00 Jul 30 '17 at 00:23
  • I don't see that assignment in the question. The OP is using `push`. – Mark Jul 30 '17 at 00:24
  • @Mark_M - It's in my solution. That's why **I'm** using `let`. The OP is not very clear about exactly what their problem is so I'm showing a couple of proper ways to process async results from a loop. I don't honestly know exactly what the OP is doing wrong. They don't show enough code or communicate clearly the wrong result they get. Since they are complaining about getting all results with the end of the loop, it is probably an issue related to trying to use a loop index in an async callback (even though they don't show that). So, I wanted to teach about using `let` as a solution to that. – jfriend00 Jul 30 '17 at 00:26
  • @Mark_M - And, I wanted to show a couple of proper ways to track async results from a loop. – jfriend00 Jul 30 '17 at 00:29
  • I downvoted because, personally, I don't really think the answer is helpful in terms of explaining what the issue is and why using Promises help solve it. The issue is a well known gotcha in JavaScript and existed well before Promises did - it can be explained & solved in a shorter more concise answer IMO – James Jul 30 '17 at 00:56
  • @James - Did you see my first solution that has nothing to do with Promises. I challenge you to post a non-promise answer that gets you all the results in order and keeps track of all errors in significantly less code than the first answer here. The OP's question is very unclear and does not even really explain what their error is. I was showing how one handles async operations in a loop using their own pseudo-code and gives you all results in order. Not much more can be done given the poor question (other than just downvote the question), but I was trying to be as helpful as possible. – jfriend00 Jul 30 '17 at 02:15
  • @James - I added some more textual explanation at the "guess" for what the OP might be doing wrong (since the question is lacking detail). Also, I think promises are relevant here. If you're keeping track of N async operations and want all their results in order, it's pretty much always simpler to code with promises. Thus, why I reference and show that option too. I think that is worth teaching. This is not ONLY a loop issue. It's also an issue with coordinating N async operations and keeping results in order. – jfriend00 Jul 30 '17 at 02:20
  • @James - And, thanks for responding about the downvote. I can at least try to understand your objection and improve the answer. I wish more downvotes came with feedback that could be used to improve the answer. – jfriend00 Jul 30 '17 at 02:22
  • @jfriend00 +1 for improving your answer, I already voted to close the question tbh for a number of reasons. Deliberately closed as a dup of another question with a very detailed explanation of the scoping issue (albeit the code actually looks ok) – James Jul 30 '17 at 02:33
  • @nadhem - Does this answer your question? If so, you can indicate that to the community by clicking the green checkmark to the left of the answer and that will also earn you some reputation points. – jfriend00 Aug 18 '17 at 06:10