'Promise.resolve await-sync value is undefined
The Promise vs async/await continues to plague my understanding of these concepts. I have read most of the answers here, and they really helped me get this far. However, I think this question might be a shade different than the rest.
The basic concept here is take a image after object detection, splice the box into a cropped image, feed that image into tesseract, and then do something with the value.
Everything seems to work fine until I try to do something with the tesseract extracted value, at which point the value either vanishes, or reports a Promise followed by undefined, or is just straight-up undefined. Basically, I can see what I want on console.log, but I just cannot use it.
It begins the object detection:
model.executeAsync(input).then(res => {
const [boxes, scores, classes, valid_detections] = res;
...
detections.push({ name: name, confidence: confidence, x1: x1, y1: y1, x2: x2, y2: y2, cpx: cpx, cpy: cpy, width: width, height: height, angle: null, textEx: null, valid: false })
}
Then I send the original image, and the box detection data to the frame operations section:
const normalizedPredictions = normalizePredictions(detections, imgSize);
setPredictions(normalizedPredictions);
getFrameData(imageElement, normalizedPredictions)
then splice out the image in this case it's a single line of numbers and letters embedded on an image.
if (frameData[i].name.indexOf('image_crop') !== -1) {
const textEx = TextFromImage(image, frameData[i])
frameData[i].textEx = textEx
imageCrop.push(frameData[i])
}
then I crop and do some image preprocessing to give tesseract a reasonable chance at an extraction. As a note: sending the dataURL to the console is a huge time saver when fine tuning the preprocessing steps. Just put it in an <img src="dataURL"></img> and you can see the cropped image without all the code to bring it into a real canvas on your app.
if (frameData.name === "image_crop") {
//create and offscreen canvas and crop the image
var crop = document.createElement('canvas');
var crop_x = crop.getContext('2d');
...
//draw the cropped image to the crop canvas
crop_x.drawImage(imageElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, destWidth, destHeight);
//extract the image data from the canvas
const imageProcessing = crop_x.getImageData(0, 0, crop.width, crop.height);
...
PreProcessFunctions(imageProcessing.data, frameData.name, destWidth, destHeight, grey, blur, dilate, invert, thresh, replace, threshLevel, rLevelMIN, rLevelMAX, gLevelMIN, gLevelMAX, bLevelMIN, bLevelMAX)
//put the processed image data back on the canvas and extract the URL
crop_x.putImageData(imageProcessing, 0, 0);
const dataUrl = crop.toDataURL("image/jpeg");
//console.log(dataUrl)
ocrImage(dataUrl, frameData.name)
}
Then I do the ocr on the image with tesseract v. 4.10
async function ocrImage(buffer, type) {
...
const worker = createWorker({ log_config });
...
(async () => {
await worker.load(); // 1
await worker.loadLanguage('eng'); // 2
await worker.initialize('eng');
//configuration for image_crop
...
const {
data: {
text,
},
} = await worker.recognize(buffer, { config }); // 3
//post text extraction processing for image_crop
if (type == "image_crop") {
...
frameData.value = parseFloat(await image_crop.substring(1)) //strip $ and return just the numerical value
frameData.textEx = await image_crop // the extracted string
}
await worker.terminate(); // 5
then the value gets returned back to the frame snapshot organization section
Promise.resolve(frameData).then(value => {
return Promise.resolve(value)
})
})();
}
}
which is then combined with other object detection data, and returned back to the canvas section.
return Promise.resolve(finalFrame)
Now to the juicy bits - sorry for the summary of what happened before, but it might be useful to figuring out what I'm doing wrong. recall:
const normalizedPredictions = normalizePredictions(detections, imgSize);
setPredictions(normalizedPredictions);
getFrameData(imageElement, normalizedPredictions)
When I console out the final response:
async function getFrameData(imageElement, normalizedPredictions) {
frameData = await TablePlay(imageElement, normalizedPredictions)
console.log(frameData)
}
this is what it looks like:
Array(4) [ {…}, {…}, {…}, {…} ] ... 2: Object { imageCrop: (1) […] } imageCrop: Array [ {…} ] 0: Object { name: "image_crop", confidence: "0.97", x1: 443.90777403116226, … } ... name: "image_crop" value: 0.43 textEx: "$0.43" ... <prototype>: Object { … } length: 1 <prototype>: Array [] <prototype>: Object { … } ... length: 4 <prototype>: Array []
What I want is value: so I can do some math on it. I can see what I want, it's right there. But it's really not, because when I just try to grab it, value becomes undefined.
async function getFrameData(imageElement, normalizedPredictions) {
frameData = await Receipt(imageElement, normalizedPredictions)
console.log("1", frameData)
console.log("2", frameData[2].imageCrop)
console.log("3", frameData[2].imageCrop[0].value)
console.log("4", Promise.resolve(frameData[2].imageCrop[0].value))
const p = Promise.resolve(frameData)
p.then(data => {
console.log("5", data)
console.log("6", data[2].imageCrop)
console.log("7", data[2].imageCrop[0].value)
console.log("8", Promise.resolve(data[2].imageCrop[0].value))
}).catch(err => {
console.log(err)
})
}
Here is the console output
1
Array(4) [ {…}, {…}, {…}, {…} ]
2: Object { imageCrop: (1) […] }
imageCrop: Array [ {…} ]
0: Object { name: "image_crop", … }
...
name: "image_crop"
potVal: 0.43
textEx: "$0.43"
...
<prototype>: Object { … }
length: 1
<prototype>: Array []
<prototype>: Object { … }
...
length: 4
<prototype>: Array []
2
Array [ {…} ]
0: Object { name: "image_crop", … }
...
name: "image_crop"
potVal: 0.43
textEx: "$0.43"
...
<prototype>: Object { … }
length: 1
<prototype>: Array []
3
Object { name: "image_crop", … }
...
name: "image_crop"
//Note: value: 0.43 - this *sometimes not shown
//Note: textEx: "$0.43" - this *sometimes not shown
...
<prototype>: Object { … }
4 undefined
5
Promise { <state>: "fulfilled", <value>: undefined }
<state>: "fulfilled"
<value>: undefined
<prototype>: Promise.prototype { … }
//Note (6-10) are identical console logs to (1-5)
Question: How do I get value:? I would like to do some math with it.
EDIT: I am pretty sure this is a timing thing as there are a few slow things (from a javascript point of view) preceding a response of value: I tested this with the browser version of the sleep timer.
async function getFrameData(imageElement, normalizedPredictions) {
frameData = await Receipt(imageElement, normalizedPredictions)
console.log("1", frameData)
console.log("2", frameData[2].imageCrop)
console.log("3", frameData[2].imageCrop[0])
console.log("4a", frameData[2].imageCrop[0].value)
for (let i = 0; i <= 10; i++) {
if (frameData[2].imageCrop[0].value == undefined) {
await sleep(1000 * i);
} else {
let timer = (i+1)*1000
console.log("4b", frameData[2].imageCrop[0].value, " timer: ", timer, " ms" )
i = 10
}
}
function sleep(duration) {
return new Promise((resolve) => {
setTimeout(resolve, duration)
})
}
The output looks like this:
1 Array(4) [ {…}, {…}, {…}, {…} ]
2 Array [ {…} ]
3 Object { name: "image_crop", … }
4a undefined
4b 0.43 timer: 5000 ms
5 Promise { <state>: "fulfilled", <value>: 0.43 }
I have run a few images through there, and 5000 seems to get the job done.
EDIT 2:
In my quest to not wait (await) longer than necessary. I changed the increments from 1000 to 10. Now I'm getting a result for value:, which I can actually use, at about 320ms. Why does the sleep increment block have such variability on the result? When I just wait a default 5K, it's fine. When I increment by 1K it's still 5K, but when I increment by 10, it's around 320. So naturally I changed the increment down to 1ms, and now it's running at about 85ms. Something is up. I'm just not sure what. Any help would be appreciated.
So, now I need to revise the question as why does an await-async not actually await for the requested response in this case. I can see if I put a kill timer in there, value: could go undefined (if I wanted) in the case where the kill timer occurs before the system could supply an answer for value:. However, why is not wait (long enough) in an await, the default for an await? Waiting long enough should be the point of this method, right?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
