0

Let's say I have 4 tasks

  1. Take a screenshot of a website (using some API service)
  2. Download the screenshot to your local hardDrive
  3. Upload the screenshot from the local hardDrive to a Google Cloud Bucket
  4. Use GC-Vision API to detect text on that screenshot

If the result of task #4 is equal to -1, that means I didn't find what I'm looking for, so I need to do this all over again until the result doesn't equal -1 (which probably means I found what I need).

Now some code:

const screenshot = require("./thum/takeScreenshot");
const googleVisionAPI = require("./gc-vision-api/findTextInButton");
const matchURL = /^((http[s]?|ftp):\/)?\/?([^:\/\s]+)((\/\w+)*\/)([\w\-\.]+[^#?\s]+)(.*)?(#[\w\-]+)?$/;
const UploadToGcBucket = require("./gc-storage/saveScreenshot");
const downloadImage = require("./downloadFile");

// let's wait for the bucket to replicate before searching text
function timeout(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const workHard = async () => {
  // 1. Take a screenshot
  const takeAscreenShot = async () => {
    let scShotTaken = await `https:${screenshot.thumURL}`;
    console.log("screenshot captured!!! ", scShotTaken);
    return scShotTaken;
  };

  //  2. Download it to local hard-drive
  const downloadScreenshot = async url => {
    let download = await downloadImage.downloadImage(url);
    return download;
  };

  // 3. Upload it to Google Cloud Bucket
  const uploadToGCBucket = async () => {
    let upload = await UploadToGcBucket.UploadToGcBucket();
    return upload;
  };

  // 4. Check if the appointment's button is present
  const checkMagicButton = async () => {
    // wait a little bit for replication
    await timeout(5000);

    let searchAppointment = await googleVisionAPI.findMagicButton(
      `some_picture_in_a_cloud_bucket.jpg`
    );
    return searchAppointment;
  };

  takeAscreenShot()
    .then(pic => {
      // Verify if it looks like a URL
      let verifyURL = matchURL.test(pic);

      if (!verifyURL) {
        return "The screenshot doesn't look like a valid URL, check the Thumb API";
      }

      downloadScreenshot(pic)
        .then(() => {
          uploadToGCBucket()
            .then(() => {
              checkMagicButton()
                .then(button => {
                  if (button === -1) {
                    throw new Error();
                  }
                })
                .catch(makeCatch("AI finding button ERROR"));
            })
            .catch(makeCatch("Uploading to GC Bucket ERROR"));
        })
        .catch(makeCatch("Downloading ERROR"));
    })
    .catch(makeCatch("ScreenShot ERROR"));
};

const makeCatch = msg => err => {
  console.log("Problem with " + msg, err);
  throw new Error(); // this is important- this rejects the Promise, so it stops the hardWork
};

const tryWork = () => {
  workHard().catch(error => {
    setInterval(tryWork, 5000);
  });
};

tryWork();

so...how do I call HardWork() all over again let's say in 5 minutes if I don't find what I need.

3
  • 1
    You're not using the results of any of the functions except checkMagicButton. That looks like a mistake. Is that your actual code, or psuedo-code? (can you post the actual code?) Commented Feb 17, 2020 at 2:29
  • Just use a setTimeout where that comment is Commented Feb 17, 2020 at 2:35
  • Also see how I chain the function results : takeAscreenShot() .then(pic => {some more code} Commented Feb 17, 2020 at 2:36

1 Answer 1

3

You're using async functions already, and everything looks Promise-based already, so you can make hardWork async and then await each Promise inside of it. If final task fails, reject. In the caller of hardWork, if the Promise ultimately rejects, set a timeout to call it again in 5 minutes, otherwise you're done:

const hardWork = async () => {
  // I assume this next line was psuedo-code for an API call or something
  await `https:${screenshot.thumURL}`;

  const image = await downloadImage.downloadImage();
  await UploadToGcBucket.UploadToGcBucket(image);
  await timeout(5000);
  const button = await googleVisionAPI.findMagicButton(`some_screenshot.jpg`);
  if (button === -1) {
    throw new Error();
  }
  // success
};

const tryWork = () => {
  hardWork()
    .catch((error) => {
      setTimeout(tryWork, 5 * 60 * 1000); // 5 minutes
    });
};
tryWork();

If you need different error messages for the different possible errors, I'd make a higher-order function that you can pass into .catch that logs the message, to keep the code DRY:

const makeCatch = msg => (err) => {
  console.log('Problem with ' + msg, err);
  throw new Error(); // this is important- this rejects the Promise, so it stops the hardWork
};

// ...

await downloadImage.downloadImage()
  .catch(makeCatch('downloadImage'));
await UploadToGcBucket.UploadToGcBucket(image)
  .catch(makeCatch('upload to gc'));
await timeout(5000);
const button = await googleVisionAPI.findMagicButton(`some_screenshot.jpg`)
  .catch(makeCatch('findButton'));

Live snippet:

// dummied up API calls below...
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
const downloadImage = () => new Promise(resolve => setTimeout(resolve, 200));
const uploadToGcBucket = () => new Promise(resolve => setTimeout(resolve, 200));
// the below function will resolve to -1 70% of the time, and to 0 30% of the time
const findMagicButton = () => new Promise(resolve => setTimeout(resolve, 200, Math.floor(Math.random() - 0.7)));
// dummied up API calls above...

const hardWork = async () => {
  const image = await downloadImage();
  await uploadToGcBucket(image);
  await timeout(500);
  const button = await findMagicButton(`some_screenshot.jpg`);
  if (button === -1) {
    throw new Error();
  }
  console.log('success');
};

const tryWork = () => {
  hardWork()
    .catch((error) => {
      console.log('failure, retry');
      setTimeout(tryWork, 2 * 1000); // 2 seconds
    });
};
console.log('start');
tryWork();

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the quick reply, unfortunately when I: ``` .then(button => { if (button === -1) { throw new Error(); } }) ``` When you throw the new Error it actually doesn't .catch() on: ``` const tryWork = () => { workHard().catch(error => { setInterval(tryWork, 5000); }); }; tryWork(); ``` but here: ``` .catch(makeCatch("AI finding button ERROR"));``` I have updated my code, so I think I would need to return something from workHard() function in order to catch it on the tryWork() function
Your current workHard function isn't chaining the long final Promise chain with its caller. Almost every time there's a Promise, it should either be the first Promise in its series, or it should be returned or awaited. So change to return downloadScreenshot(..., and uploadToGCBucket() to return uploadToGCBucket(), etc. Or, it would be much better to use the pattern in my answer - await each call, and the code is much cleaner (and less bug-prone)
Do you mind just putting a link to a code snippet in a much simpler code? now I'm confused. I believe I used your pattern to catch errors in a higher-order function...
See snippet in edit. The findMagicButton results in it throwing 70% of the time, so it'll probably retry a few times before you see "Success".
You're a genius @CertainPerformance, I really appreciate all your awesome help! It's flawlessly working now!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.