Promise how to implement in this situation?

2019-04-16 06:04发布

问题:

i have a problem with sync using fs.readfile, they are not inicializing at the first time, if i try request again, then i have results. I know i can use promise and i find something useful with Q from Kriskowal. I tried it but without success. I didn´t understand very well how to implement it. If someone can help with this, i will be eternally grateful.

code:

  "use strict"

 var Q = require('q');
 var fs = require('fs');
 var arrayZipBand = [];
 var jsonZipCode = [];
 var arrayParsedZipcodeBr = [];

 exports.parse = function(opts) {

if (opts.zipBand.constructor != Array) {
opts.zipBand = [opts.zipBand];
}
if (opts.location.constructor != Array) {
opts.location = [opts.location];
}

if (opts.config === 0) {
opts.zipBand.forEach(function(file) {
  fs.readFile(file, 'utf8', function(err, logData) {
    if (err) throw err;
    let text = logData.toString();
    decodeURIComponent(text);

    let lines = text.split('\n');

    lines.forEach(function(line) {
      let parts = line.split('@');

      if (parts[1] != undefined) {
        let obJson = {
          LOC_NU: parts[0],
          LOC_CEP_INI: parts[1],
          LOC_CEP_FIM: parts[2]

        }
        arrayZipBand.push(obJson);
      }

    });


  });

});

opts.location.forEach(function(file) {
  fs.readFile(file, 'utf8', function(err, logData) {


    if (err) throw err;

    let text = logData.toString();
    decodeURIComponent(text);

    let lines = text.split('\n');

    lines.forEach(function(line) {
      let parts = line.split('@');

      if (parts[1] != undefined) {
        for (let i = 0; i < arrayZipBand.length; i++) {
          if (parts[0] == arrayZipBand[i].LOC_NU) {
            jsonZipCode.push(arrayZipBand[i]);
          }
        }
        if (jsonZipCode === undefined) {
          throw "Was not possible to find Zipcode for the id " + parts[0];
        }

        for (let i = 0; i < jsonZipCode.length; i++) {
          let obJson = {
            LOC_NU: parts[0],
            UFE_SG: parts[1],
            LOC_NO: parts[2],
            MUN_NU: parts[8],
            LOC_CEP_INI: jsonZipCode[i].LOC_CEP_INI,
            LOC_CEP_FIM: jsonZipCode[i].LOC_CEP_FIM
          }

          arrayParsedZipcodeBr.push(obJson);
        }
        jsonZipCode = [];

      }


    });
  });
});



};
 return arrayParsedZipcodeBr;

}

回答1:

uhm it seems you are trying to read files in the loop cycle then, but in a async way. First question, is why async reading those files? You can always read them in sync way:

var data=fs.readFileSync(fname, encoding);

By the way, if you wish to read them async and keep the for...loop you need something like a Promise, or a timed wait or a more complex synchronization mechanism.

You can keep it simple, without using any other packages/modules in this way:

/**
       * Promise.All
       * @param items Array of objects
       * @param block Function block(item,index,resolve,reject)
       * @param done Function Success block
       * @param fail Function Failure block
       * @example

          promiseAll(["a","b","c"],
          function(item,index,resolve,reject) {
            MyApp.call(item,function(result) {
              resolve(result);
            },
            function( error ) { reject(error); }):
          },
          function(result) { // aggregated results

          },function(error) { // error

          })

        * @author Loreto Parisi (loretoparisi at gmail dot com)
       */
    promiseAll: function(items, block, done, fail) {
            var self = this;
            var promises = [],
                index = 0;
            items.forEach(function(item) {
                promises.push(function(item, i) {
                    return new Promise(function(resolve, reject) {
                        if (block) {
                            block.apply(this, [item, index, resolve, reject]);
                        }
                    });
                }(item, ++index))
            });
            Promise.all(promises).then(function AcceptHandler(results) {
                if (done) done(results);
            }, function ErrorHandler(error) {
                if (fail) fail(error);
            });
        }, //promiseAll

so you can call it like

promiseAll(arrayOfItems, function(item, index, resolve, reject) {
    // do something on item
    if (someSuccessCondOnThisItem) {
        resolve(item)
    } else {
        reject(new Error("operation failed"))
    }
}, function(results) { // aggregated results

    console.log("All done %d", results.length);

}, function(error) { // error
    console.log(error.toString());
});

Keep in mind that this is a very simplified approach, but in most of cases it works when cycling through arrays.

Here is a simple working example in the playground:

var console = {
 log : function(s) { document.getElementById("console").innerHTML+=s+"<br/>"}
}
var promiseAll= function(items, block, done, fail) {
            var self = this;
            var promises = [],
                index = 0;
            items.forEach(function(item) {
                promises.push(function(item, i) {
                    return new Promise(function(resolve, reject) {
                        if (block) {
                            block.apply(this, [item, index, resolve, reject]);
                        }
                    });
                }(item, ++index))
            });
            Promise.all(promises).then(function AcceptHandler(results) {
                if (done) done(results);
            }, function ErrorHandler(error) {
                if (fail) fail(error);
            });
        }; //promiseAll

arr=[1,2,3]
promiseAll(arr
                ,function(item,index,resolve,reject) {
                  console.log("Resolving item[" + index+"]")
                  var okCond=true
                  if(okCond) {resolve(item)} else { reject(new Error("item[" + index+"]")) }
                }
                ,function(results) { // aggregated results
console.log("All done of "+results.length);
                }
                ,function(error) { // error
                console.log(error);
 });
<div id="console"/>

Finally, a complete asynchronous example, showing how to defer execution of a XMLHttpRequest, when cycling through a list. The ExecutionBlock is calling reject and resolve after the SimpleRequest responds, causing the Promise the wait its execution before calling the then.

var console = {
    log: function(s) {
      document.getElementById("console").innerHTML += s + "<br/>"
    }
  }
  // Simple XMLHttpRequest
  // based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
      var request;
      if (window.XMLHttpRequest) { // Mozilla, Safari, ...
        request = new XMLHttpRequest();
      } else if (window.ActiveXObject) { // IE
        try {
          request = new ActiveXObject('Msxml2.XMLHTTP');
        } catch (e) {
          try {
            request = new ActiveXObject('Microsoft.XMLHTTP');
          } catch (e) {}
        }
      }
      // state changes
      request.onreadystatechange = function() {
        if (request.readyState === 4) { // done
          if (request.status === 200) { // complete	
            response(request.responseText)
          } else response();
        }
      }
      request.open('GET', what, true);
      request.send(null);
    }
  }
  //PromiseAll
var promiseAll = function(items, block, done, fail) {
  var self = this;
  var promises = [],
    index = 0;
  items.forEach(function(item) {
    promises.push(function(item, i) {
      return new Promise(function(resolve, reject) {
        if (block) {
          block.apply(this, [item, index, resolve, reject]);
        }
      });
    }(item, ++index))
  });
  Promise.all(promises).then(function AcceptHandler(results) {
    if (done) done(results);
  }, function ErrorHandler(error) {
    if (fail) fail(error);
  });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
  SimpleRequest.call('https://icanhazip.com/', function(result) {
    if (result) {
      console.log("Response[" + index + "] " + result);
      resolve(result);
    } else {
      reject(new Error("call error"));
    }
  })
}

arr = [1, 2, 3]
promiseAll(arr, function(item, index, resolve, reject) {
  console.log("Making request [" + index + "]")
  ExecutionBlock(item, index, resolve, reject);
}, function(results) { // aggregated results
  console.log("All response received " + results.length);
  console.log(JSON.stringify(results));
}, function(error) { // error
  console.log(error);
});
<div id="console" />