节点的异步问题

时间:2013-06-27 16:26:53

标签: node.js asynchronous

我有一个在节点应用程序中运行的函数,由于我对如何正确编写异步代码缺乏了解而无法工作。下面是一个功能,它接收带有电子邮件的个人资料。我想遍历每封电子邮件并检查该用户是否存在于我的数据库中。如果他们这样做,我想返回给定的回调并完全存在该函数而不做任何其他事情。如果找不到用户,我会根据配置文件中给出的信息创建一个新用户,然后返回与新创建的用户相同的回调。截至目前,该功能按预期工作,但即使已在我的数据库中找到用户,它也会创建一个新用户。 ('User'变量在上面定义并具有'create'功能。另外,我想尽可能避免使用'async'节点模块。

function processProfile(profile, callback) {
        var existingUser;
        if (profile.emails) {
            profile.emails.forEach(function(email) {
                console.log("Searching for user with this email:" + email.value);
                existingUser = findUserByEmail(email.value);
                if (existingUser) {
                    console.log("Found the existing user");
                    return callback(null, existingUser);
                }
            });
            if(!existingUser){
                console.log("Creating new user");
                var newUser = {
                    id: profile.id,
                    firstName: profile.name.givenName,
                    lastName: profile.name.familyName,
                    email: profile.emails[0].value
                };
                user.create(newUser, profile.provider, function(err, user) {
                    if (err) throw err;
                    return callback(null, user);
                });
            }
        }
    }

1 个答案:

答案 0 :(得分:1)

这有什么问题吗?

function processProfile(profile, callback) {
    var existingUser;
    var index = 0;

    function processNextEmail() {
        if(index >= profile.emails.size()) return; //When we've popped nothing exit
        var email = profile.emails[index++];
        console.log("Searching for user with this email:" + email.value);
        existingUser = findUserByEmail(email.value);
        if (existingUser) {
            console.log("Found the existing user");
            callback(null, existingUser);
            processEmail();//recursive call to prcess the next email
        } else {
            console.log("Creating new user");
        var newUser = {
            id: profile.id,
            firstName: profile.name.givenName,
            lastName: profile.name.familyName,
            email: profile.emails[0].value
        };
        user.create(newUser, provider, function(err, user) {
            if (err) throw err;
            callback(null, user);
            processNextEmail();//recursive call to process the next email after creating the user and adding it to the database.
        });
        }
    }

    processNextEmail();
}

如果您需要递归逻辑来不删除电子邮件,您可以在processProfile()闭包范围内进行涉及indice的简单修改。

另请注意,返回callback()行,并没有真正做任何事情。从异步发生的函数返回是浪费时间。只需调用回调,然后你可以调用一个空的返回来跳过函数的其余部分,如果你愿意,但它是不必要的,除非返回影响逻辑流。

编辑:事实证明这个例子太简单了,不太有趣。下面的代码我作为一些例子,用于工作中无法掌握异步代码的人。有一次我认为在节点中使用同步代码是可以的,用于收集配置数据。让我们假装我们将配置存储在一个文件中,然后从该文件中获取文件名,并从另一个文件中收集另一层配置数据。我们可以使用readFileSyn或使用readFile这两种方式。异步版本很棘手,因为我们需要等待第一步完成,因为我们必须从第一个文件中获取文件名,以便知道第二个文件的存储位置。以下是同步解决方案和异步解决方案的代码。

//The synchronous way
function useConfigurationData(configData) {
    dosomethinginterestingwith(configData);
}

function getConfigurationData(fileName) {
    var fileName2 = fs.readFileSync(fileName);
    var configurationData = fs.readFileSync(fileName2);
    return configurationData;
}

var fileData = getConfigurationData('someFile');
useConfigurationData(fileData);


//The equivalent async way
function getConfigurationData(fileName, useConfigDataCallBack) {
    fs.readFile(fileName, getConfigDataStepTwo);

    function getConfigDataStepTwo(err, fileName2) {
        fs.readFile(fileName2, getConfigDataStepThree);
    }

    function getConfigDataStepThree(err, fileData) {
        useConfigDataCallBack(fileData);
    }
}

getConfigurationData('someFile', useConfigurationData);

请注意,我们提供给getConfigurationData的回调是最后一步。我们也可以只依赖于全局定义的getConfigurationData函数,但是作为回调传递它是更好的风格。

我喜欢这种语法,是第二个getConfigurationData函数的代码按顺序读取,非常同步。但是如果你遵循逻辑流程,它就会被运行为异步。它易于阅读并遵循节点异步I / O模型。在配置数据的情况下,我认为同步选项是可以接受的,但这仍然是如何从异步回调中获取同步行为和语法(ish)的一个很好的演示。