Search code examples
javascriptpromisebluebird

Loop, DNS and Bluebirdjs - promise still works asynchronouse


I have a little problem with my function that should check if domains with the specific name are available under few top level domains. When I call this function like whois("example434"); it returns undefined instead of array ["example424.org", ..., "example424.com"] . How to modify the function to solve this problem? Where I make mistake?

function whois (name, domains = '*') {
    if ( typeof domains == 'Object' && domains.length > 0 ) {
      var TLD = domains;
    } else {
      var TLD = ['.com','.net','.org','.edu','.gov', '.biz', '.info', '.mobi', '.me', '.tv', '.info', '.io'];
    }

    var available = [];
    Promise.resolve(
      Promise.map(TLD, function(domain) {
        return dns.resolve4(name + domain, (err, addresses) => {
          if (!addresses || addresses.length <= 0 ){
            console.log(name + domain);
            available.push(name + domain);
          }
        });
      })
    ).then(()=>{
      console.log(available);
      return available;
    });
}


Solution

  • Your main issues are:

    1. dns.resolve4 doesn't look like it returns a promise, since it uses nodejs callback pattern, since it doesn't return a promise, Promise.map won't wait for the asynchronous code to complete
    2. Why wrap Promise.map in Promise.resolve - makes no sense since Promise.map returns a promise
    3. the funciton whois never returns a value, hence you get undefined
    4. typeof domains can never be Object, did you mean object - even that is not a good way to determine if a vlaue is an Array

    Your title includes the phrase: promise still works asynchronous - well, yes, were you expecting something else? Asynchronous code is asynchronous, and nothing will change that

    Anyway, your code with the issues annotated:

    function whois(name, domains = '*') {
        // Issue 4
        if (typeof domains == 'Object' && domains.length > 0) {
            var TLD = domains;
        } else {
            var TLD = ['.com', '.net', '.org', '.edu', '.gov', '.biz', '.info', '.mobi', '.me', '.tv', '.info', '.io'];
        }
    
        var available = [];
        // Issue 2
        Promise.resolve(
            Promise.map(TLD, function(domain) {
                // Issue 1
                return dns.resolve4(name + domain, (err, addresses) => {
                    if (!addresses || addresses.length <= 0) {
                        console.log(name + domain);
                        available.push(name + domain);
                    }
                    // possible error: if there's an error (err is not falsey) does that really mean the domain is available?
                });
            })
        ).then(() => {
            console.log(available);
            return available;
        });
        // Issue 3
    }
    

    You'll want to promisify the dns.resolve4 function, const resolve4 = in the code below.

    I know that bluebirdjs and later nodejs have built in promisify methods however the logic for those would mean an err rejects the promise which you probably do not want.

    Also, you want to resolve differently for different successful results, so, best to promisify "by hand" as it were

    Use Array.isArray to determine if TLD is an Array

    function whois (name, TLD) {
        // if TLD isn't an array, use the default array
        if (!Array.isArray(TLD)) {
            TLD = ['.com','.net','.org','.edu','.gov', '.biz', '.info', '.mobi', '.me', '.tv', '.info', '.io'];
        }
    
        // prepare the list of full domains to check
        const domains = TLD.map(domain => name + domain);
    
        const resolve4 = domain => new Promise(resolve => {
            dns.resolve4(domain, (err, addresses) => {
                if (addresses && addresses.length) {
                    // domain resolves - so it's not available
                    resolve(false);
                }
                // uncomment the next two lines if you want an error to indicate the domain is NOT available
                //else if (err) {
                //    resolve(false);
                } else {
                    // it's available
                    resolve(domain);
                }
            });
        });
    
        return Promise.map(domains, resolve4)
        // filter for available domains (i.e. return an array of non-falsey results)
        .then(results => results.filter(domain => domain));
    }