简体   繁体   中英

How to use async/await to get input from user but wait till entire condition statement is read before resolving?

I created a function that prompts the user for a URL, then validates that the entered input is actually a URL. The issue is, when I call this function from my other function, I have been able to get it to wait for user input before but it doesn't go through the entire condition statement before returning the url.

How do I use promises/async/await to wait till my requestSiteURL() function has completed its condition statement, before setting the variable in getURL()?

const readline = require('readline').createInterface({
   input: process.stdin,
   output: process.stdout,
});

// request url from user in command prompt
const requestSiteURL = () => {
  return new Promise((resolve, reject) => {
    readline.question('Please type url: ', async (url) => {
      if (validUrl.isUri(url)) {
        readline.close();
        resolve(url);
        // if user types no, then use base url
      } else if ('No' || 'no' || 'NO') {
        url = URL;
        resolve(url);
      } else {
        reject(
          console.log(
            'Please type in a valid URL or type "no" to use base url.'
          )
        );
        requestSiteURL();
      }
    });
  });
};

// grabs all urls from original link and returns array
const getURLS = async () => {
  let url = await requestSiteURL();
  url = deleteFowardSlash(url);
  try {
    const res = await axios.get(url);
    const data = res.data;
    const $ = cheerio.load(data);

    const urlQueue = [];

    // finds all absolute links and pushs to urlQueue array
    $("a[href^='http']").each((i, elem) => {
      let link = $(elem).attr('href');
      //checks to make sure link isnt any duplicate links in urlqueue array
      link = deleteFowardSlash(link);
      uniqueLinkChecker(link, urlQueue);
    });
    return urlQueue;
  } catch (err) {
    console.error(err);
    return response.status(400).send(err);
  }
};

I think it is a matter of syntax. Here I switched the original line to use the Promise constructor correctly. Promise mdn

// request url from user in command prompt
const requestSiteURL = () => {
  return new Promise((resolve, reject) => {
    readline.question('Please type url: ', async (url) => {
      if (validUrl.isUri(url)) {
        readline.close();
        resolve(url);
        // if user types no, then use base url
      } else if (url.toLowerCase() === 'no') {
        url = URL;
        resolve(url);
      } else {
        console.log(
          'Please type in a valid URL or type "no" to use base url.'
        )
        requestSiteURL().then(resolve);
      }
    });
  });
};

Edit: Adding an async/await version. Basically I think you don't ever have to reject. I edited the above to not reject as well. Also I'm assuming URL is the default url (this conflicts with the global URL namespace, just for the record)

// request url from user in command prompt
const requestSiteURL = async function () {
  const url = await new Promise(resolve => {
    readline.question('Please type url: ', resolve)
  })
  if (validUrl.isUri(url)) {
    readline.close()
    return url
  } else if (url.toLowerCase() === 'no') {
    return URL
  } else {
    console.log('Please type in a valid URL or type "no" to use base url.')
    return requestSiteURL()
  }
}

At first glance it doesn't look like your 'else' condition will wait at all. You reject the promise and recursively call requestSiteURL() , but do nothing with the promise that is returned. This will ask the question again, but the calling function has no way to access that promise. The original promise that was returned has already been rejected. The new promise is not referenced anywhere, so when resolve or reject is called for the second time, there is no code to access the result.

If you want your code to fail and reject the returned promise, then you shouldn't call it again in the else statement.

If you do want the one function to keep asking the question until a valid answer is entered, you could fix that issue by encapsulate your code in a function and keep calling it on an error until it resolves with the original 'resolve' parameter of the promise you return. It doesn't seem like readline.question() takes an async function either:

// request url from user in command prompt
const requestSiteURL = () => {
  return new Promise((resolve, reject) => {
    const askQuestion = () => {
      readline.question('Please type url: ', (url) => {
        if (validUrl.isUri(url)) {
          readline.close(); // close readline here?
          resolve(url);
        } else if ('No' || 'no' || 'NO') {
          url = URL;
          // don't close readline here?
          resolve(url);
        } else {
          console.log(
            'Please type in a valid URL or type "no" to use base url.'
          );
          // call askQuestion() again until we resolve with something
          askQuestion()
        }
      });
      
      // call askQuestion() the first time
      askQuestion();
  });
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM