异步/等待和承诺

时间:2018-03-23 16:44:48

标签: javascript node.js asynchronous promise async-await

我在使用节点v8.1使用async / await时遇到问题。看来我的问题是我没有从异步函数返回一个promise。这导致程序流程无序运行。我想通过创建函数async该函数自动返回一个promise,但事实并非如此。

我希望下面的程序输出:

Validating xlsx file...

(text from validateParsedXlsx)

Adding users to cognito...

(text from addUsersToCognito)

Adding users to dynamodb...

(text from addUsersToDynamodb)

相反,我得到:

Validating xlsx file...

Adding users to cognito...

Adding users to dynamodb...

(text from validateParsedXlsx) 

(text from addUsersToCognito)  

(text from addUsersToDynamodb)

问题似乎非常明显,validateParsedXlsx() addUsersToCognito()addUsersToDynamodb()没有返回承诺。我再次认为,通过使用async关键字,该函数会自动处理此问题。

感谢您的帮助。

这是我的剧本:

const xlsx = require('xlsx');
const AWS = require('aws-sdk');

AWS.config.update({region: 'us-west-2'});
const documentClient = new AWS.DynamoDB.DocumentClient({convertEmptyValues: true});

async function main(){

    if (!process.argv[2]) {
        console.log('\nAbsolute filepath missing. Pass the absolute filepath in as command line argument.\n')
        process.exit(1);
    }

    const xlsxFilePath = process.argv[2];
    let parsedXlsx = [];
    try {
        parsedXlsx = parseXlsx(xlsxFilePath);
    } catch (error) {
        if(error.code === 'ENOENT') {
            console.log(`\nThe file path: ${process.argv[2]} cannot be resolved\n`)
        } else {
            console.log(error);
        }
    }

    console.log('\n\nValidating xlsx file...\n');
    await validateParsedXlsx(parsedXlsx);
    console.log('\n\nAdding users to cognito...\n');
    await addUsersToCognito(parsedXlsx);
    console.log('\n\nAdding users to dynamodb...\n');
    await addUsersToDynamodb(parsedXlsx);

}

function parseXlsx(filePath) {

    const workbook = xlsx.readFile(filePath);
    const sheetNameList = workbook.SheetNames;

    const parsedXlsxSheets = sheetNameList.map(function (y) {
        const worksheet = workbook.Sheets[y];
        const headers = {};
        const data = [];

        for (z in worksheet) {
            if(z[0] === '!') continue;
            //parse out the column, row, and value
            const col = z.substring(0,1);
            const row = parseInt(z.substring(1));
            const value = worksheet[z].v;

            //store header names
            if(row == 1) {
                headers[col] = value;
                continue;
            }

            if(!data[row]) data[row] = {};
            data[row][headers[col]] = value;
        }
        //drop those first two rows which are empty
        data.shift();
        data.shift();
        return data;
    });

    return parsedXlsxSheets[0]
}

async function validateParsedXlsx(users) {

    let error = false;
    users.forEach(async (user, index) => {
        if (!user.email) {
            console.log(`User at row ${index + 2} doesn't have 'email' entry in xlsx file.`);
            error = true;
        }
        if (!user.displayName) {
            console.log(`User at row ${index + 2} doesn't have 'displayName' entry in xlsx file.`);
            error = true;  
        }
        if (!user.serviceProviderId) {
            console.log(`Userat row ${index + 2} doesn't have 'displayName' entry in xlsx file.`);
            error = true;
        } else {
            const params = {
                TableName: 'service-providers',
                Key: {
                    serviceProviderId: user.serviceProviderId
                }
            }

            const response = await documentClient.get(params).promise();
            if (!response.Item) {
                console.log(`User at row ${index +2} does not have a valid serviceProviderId.`);
                error = true;
            } else {
                console.log(`User ${user.email} is valid, assigned to service provider: ${response.Item.displayName}`);
            }
        }

        if (error) {
            console.log(`Every user in xlsx file must have these attributes, spelled correctly: email, displayName, and serviceProviderId\n\nIn addition, make sure the serviceProviderId is correct by checking the service-providers dynanomdb table.`);
            process.exit(1);
        }
    });

}

async function addUsersToCognito(users) {

    const cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();

    const results = await cognitoIdentityServiceProvider.listUserPools({MaxResults: 10}).promise();

    let serviceProviderUserPoolId = '';

    results.UserPools.forEach((userPool) => {
        if(userPool.Name === 'service-provider-users') {
            serviceProviderUserPoolId = userPool.Id;
        }
    });

    users.forEach(async (user) => {

        const params = {
            UserPoolId: serviceProviderUserPoolId,
            Username: user.email,
            DesiredDeliveryMediums: ['EMAIL'],
            TemporaryPassword: 'New_User1',
            UserAttributes: [
                {
                    Name: 'email',
                    Value: user.email
                },
                {
                    Name: 'custom:service_provider_id',
                    Value: user.serviceProviderId
                }
            ]
        }

        try {
            await cognitoIdentityServiceProvider.adminCreateUser(params).promise();
            console.log(`Added user ${user.email} to cognito user pool`);
        } catch (error) {
            if (error.code === 'UsernameExistsException') {
                console.log(`Username: ${user.email} already exists. No action taken.`);
            }
            else {
                console.log(error);
            }
        }
    });

}

async function addUsersToDynamodb(users) {

    users.forEach(async (user) => {
        const params = {
            TableName: 'service-provider-users',
            Item: {
                serviceProviderId: user.serviceProviderId,
                userId: user.email,
                displayName: user.displayName,
                isActive: false,
                role: 'BASIC'
            },
            ConditionExpression: 'attribute_not_exists(userId)'
        }

        try {
            await documentClient.put(params).promise();
            console.log(`Added user ${user.email} to dynamodb user table`);
        } catch (error) {
            if (error.code === 'ConditionalCheckFailedException') {
                console.log(`User ${user.email} already in the dynamodb table service-provider-users`);
            } else {
                console.log(error);
            }
        }
    });

}

main();

1 个答案:

答案 0 :(得分:2)

  users.forEach(async (user, index) => {

这开始了一些有希望的行动,但从未等待他们。可以这样做:

  await Promise.all(users.map(async (user, index) => {

...并行执行它们或执行此操作:

  await users.reduce((chain, user, index) => async (user, index) => {
   await chain;
   //...
  }, Promise.resolve());

一个接一个地执行它们。

PS:使用process.exit应该是结束程序的最后一个选项