Returning status values after using Promise for onload and onerror

Viewed 773

I am trying to see if a page/img successfully loads using javascript onload and onerror. I am attempting to read the status variable but am not able to do so when I assign the variable to read the status. I am attempting to use promises as outlined in the possible answers provides but still have some confusion.


const validateInput = (input) => {
  const errors = {};
  ... 

    if(!(isImgURlValid(input)))
    {
      errors = `wrong image'` 
    }
    ... 
  return errors;

const isImgURlValid = (path) => {
  let img = document.createElement('img');
  img.src = path;  
  let valid
 const promise = new Promise(resolve => {
    const img = new Image();
    img.onload = () => resolve({path, "status": 'ok'});
    img.onerror = () => resolve({path, "status": 'error'});
     img.src = path;
});
promise.then(function(val) { 
  console.log(val); 
  valid = val.status
}); 
console.log (valid)
}

//when I use async, my render functions no long render the errors properly
export const renderImgUrlInput = ({ input, label, type, size, required, meta: { touched, error } }) => (
  <div className={
    cs('form-group', size, {
      'has-error': touched && error,
      'required-input' : required
    })
  }>
    <label className="control-label" htmlFor={input.name}>{label}</label>
    <input {...input} placeholder={required ? 'Required' : ''} className="form-control" /> 
    {touched && error &&
      <span className="help-block">{error}</span>
    }
    {touched && !error &&
      <h1 className="help-block">{error} 'Image worked'</h1>
    }
  </div>
)

3 Answers

const isImgURLValid = (path) => {
    return new Promise((resolve, reject) => {
        const img = document.createElement("img");
        img.src = path;
        img.onload = resolve;
        img.onerror = reject;
        img.src = path;
        document.body.appendChild(img);
    });
};

isImgURLValid("https://www.gravatar.com/avatar/206601a888686677c4a74c89d9a2920f?s=48&d=identicon&r=PG")
    .then(() => console.log("Path is valid"))
    .catch(() => console.error("Path isn't valid"))

I guess you might be a bit new to promises, indicating you already read the suggested duplicate, but you can formulate your code anew in the following way

const isImgUrlValid = (path) => {
  return new Promise( (resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve({path, "status": 'ok'});
    img.onerror = () => reject({path, "status": 'error'});
     img.src = path;
  });
};

// when it's found, resolve got called and we know it succeeded
isImgUrlValid('https://www.gravatar.com/avatar/153828e74e3fb5f7aeb19a28a78a378a?s=32&d=identicon&r=PG&f=1').then( status => console.log('image found') );

// when it's not found reject was called, and we need to catch it
isImgUrlValid('invalid_uri').then( _ => console.log('I will never be called') ).catch( err => console.log('no image found') );

This would then use resolve (success) and reject (failure) to use the normal flow of how promises work.

By returning the promise, any consumer can then use the then or catch chain to work with success or failures

Say you want to use the above code in a function, then it changes a bit, you can't really check this synchronously, unless the caller is an async function, in which case you could do it like:

const validateInput = async () => {
  let isValidImage = false;
  try {
   isValidImage = await isImgUrlValid( path );
  } catch (ex) {
    // not a correct image
    
  }
};

But this doesn't help you as now validateInput implicitly returns a promise, so then you have to handle this one as a promise.

You should return your created promise from isImgURlValid back to the caller. The caller can then wait for the promise to resolve and use the resolved value to see whether or not the provided image source was valid.

Here is an example inspired by the question code. You can enter an image source into the input box, then press Enter to trigger the event.

const isImgURlValid = (path) => { 
  return new Promise(resolve => {    
    const img = new Image()
    img.src = path;
    img.onload = () => resolve(true);
    img.onerror = () => resolve(false);
    
    // img starts loading src when added to the DOM
    document.body.append(img);
    document.body.removeChild(img);
  });
};

const validateInput = async (input) => {
  const errors = {};
  if (!await isImgURlValid(input)) {
    errors.url = "invalid";
  }
  return errors;
};

document.getElementById("input").addEventListener("change", ({target}) => {
  validateInput(target.value).then(console.log);
});
<input id="input" type="text" />

Related