0

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

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);
                });
            }
        }
    }
4

1 回答 1

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() 闭包范围内进行涉及索引的简单修改。

另请注意,return 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)的一个很好的演示。

于 2013-06-27T16:46:19.853 回答