简体   繁体   中英

In JavaScript/Node.js, how to use Async/Await

In JavaScript/Node.js, how to use Async/Await for the below code to return placeName with a,b,c & d.

test.csv is having c,d

var placeName = ["a","b"];
var csvFile = 'test.csv';
fs.readFile(csvFile, 'UTF-8', function(err, csv) {
  $.csv.toArrays(csv, {}, function(err, data) {
    for(var i=0, len=data.length; i<len; i++) {
      console.log(data[i]); //Will print every csv line as a newline
      placeName.push(data[i][0].toString());
    }
  });
   console.log(placeName); //Inside the function the array show a,b,c,d
});

// Prints only a and b. Did not add c & d from the csv.
console.log(placeName); 

Create a function returning a promise

The await operator waits for a promise to be fulfilled and can only be used within async functions - which return a promise when called. So updating place names will need to be written as a function that returns a promise. Using a single promise for all operations is possible but not recommended:

function addNames(placeName, csvFile) {
    return new Promise( (resolve, reject) => {
        fs.readFile(csvFile, 'UTF-8', function(err, csv) {
            if(err) {
               reject(err);
            }
            else {
                $.csv.toArrays(csv, {}, function(err, data) {
                    if( err) {
                        reject( err);
                    }
                    else {
                        for(var i=0, len=data.length; i<len; i++) {
                            console.log(data[i]); //print supplied data elements
                            placeName.push(data[i][0].toString());
                        }
                    }
                    resolve( placeName);
                });

            }
        });
    });
}

Although untested, this is based on the posted code: the input placeName array is modified in place. If fulfilled, the promise value is set to the modified array. However ....

Reducing indented code

Notice the code becomes more indented the more node style callback operations are added? Taken to extreme this can result in a condition known as "call back hell" - difficult to write, compile without error or maintain.

Removing nested callbacks is one of the design aims of promises and can be achieved by promisifying operations separately. The following example separates not just reading and decoding, but transforming the decoded data and adding it to an existing array:

function readCsvFile( csvFile) {
    return new Promise( (resolve, reject)  => {
        fs.readFile(csvFile, 'UTF-8',
           (err, csv) => err ? reject(err) : resolve( csv)
        );
    });
}
function decodeCsv( csv) {
    return new Promise( (resolve, reject) => {
        $.csv.toArrays(csv, {},
           (err, data) => err ? reject( err) : resolve( data)
        );
    });
}
function extractNames( data) {
   return data.map( item => item[0].toString());
}

function addNames( placeName, csvFile) {
    return readCsvFile( csvFile)
    .then(decodeCsv)
    .then(extractNames)
    .then(  // push new names to placeName and return placeName
       names => names.reduce( (a,name) => {a.push(name);return a}, placeName)
     );
}

Calling as part of a promise chain

Usage without using async/await requires adding promise handlers for fulfillment and rejection to be called asynchronously after data becomes available or an error occurs:

var placeName = ["a","b"];
var csvFile = 'test.csv';

addNames( placeName, csvFile)
.then( placeName => console.log(placeName)) // log updated places
.catch( err => console.log( err));  // or log the error.

Usage of await

As mentioned, the await operator can only be used inside async functions. It waits for fulfilled promise data so that it can be processed further inside the same function.

async function example() {

    var placeName = ["a","b"];
    var csvFile = 'test.csv';

    await addNames( placeName, csvFile);
    // do something with placeName

    return somethingElse;       
}

Adding fulfillment and rejection handlers to the promise returned by calling example has not been shown.

About return await ...

Note that a promise returned from an async function resolves the promise returned by calling the function. Hence the coding pattern of

 return await operand

contains an unnecessary await operator.

Writing async functions instead of ordinary ones (TLDR)

Using async functions in node still requires converting operations which use error/success callbacks into requests that returns a promise ("promisifying" the operation), but syntactically allows writing code processing the request to be written immediately following await operations. Code produced is similar in style to procedural code. For example:

function readCsvFile( csvFile) {
    return new Promise( (resolve, reject)  => {
        fs.readFile(csvFile, 'UTF-8',
           (err, csv) => err ? reject(err) : resolve( csv)
        );
    });
}
function decodeCsv( csv) {
    return new Promise( (resolve, reject) => {
        $.csv.toArrays(csv, {},
           (err, data) => err ? reject( err) : resolve( data)
        );
    });
}

async function addNames( placeName, csvFile) {
    let csv = await readCsvFile( csvFile);
    let arrays = await decodeCsv( csv);
    let names = arrays.map( item => item[0].toString());
    placeName.push.apply( placeName, names);
    return placeName;
}

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