【问题标题】:Modify and return an object inside an async function修改并返回异步函数内的对象
【发布时间】:2020-07-31 01:47:44
【问题描述】:

我想在异步 js 代码中修改一个对象 (userTracker)。 运行函数 (fetchUsers) 后,我想在所述对象上有一个更新的字段。 但是在修改函数中的对象后,我继续得到undefined。我怎样才能做到这一点?

我的代码:

let userTracker = {
    counter : 0,
    data : []
}

function fetchUsers(userTracker) {
    let filter  = {"some" : "conditions"}
    User.find(filter).exec() // I am deliberately not calling the `limit()` function. I have a goal to achieve
    .then( (users ) => {
        return users.forEach((user) => {

            if (userTracker.counter < 20) {
                userTracker.data.push(user)
                userTracker.counter += 1
            } else {
                return 
            }
        })
    })
    .catch(err => {
        console.log(err)
    })
}

fetchUsers(userTracker)
console.log(userTracker) // It logs `{}`. Accessing any field on the object shows `undefined`

运行fetchUsers 函数后,如何访问填充的userTracker 对象?我想以这样的方式结束

我想要达到的目标:

userTracker = {
    counter : 8,
    data : [
        user1, user2, user3, ...
    ]
}

我不擅长异步编程,非常感谢任何有关如何实现上述所需输出的帮助。

【问题讨论】:

  • 如果整个 userTracker 对象打印 undefined 我不确定这是一个异步问题。就像如果您要注释掉 fetchUsers 调用,它是否仍会打印 undefined
  • 我的错,它实际上打印了一个空对象。尝试访问其上的属性是打印 undefined

标签: javascript node.js express mongoose async-await


【解决方案1】:

answer by @shivashriganesh-mahato 是正确的,但我想解释一下为什么您的原始代码不起作用。

每当您将回调传递给函数时,该函数可能会以以下两种方式之一调用它:(1) 同步或 (2) 异步。 同步很容易理解;这意味着回调是在期间执行您将回调传递给的函数的执行。

异步 非常不同。回调的执行被推迟,直到当前正在执行的所有同步代码都完成。这包括您将回调传递给的函数、包含调用该函数的行的函数等等。回调至少会推迟到整个当前调用堆栈完成执行,之后可能还会更长。例如,当您发送网络请求并传递回调来处理响应时,回调不仅会延迟到调用堆栈清除之后,还会延迟到响应到达之后。从计算机的角度来看,这可能需要很长时间(即几毫秒)。

异步函数调用不会推送到调用堆栈的顶部。相反,它们被放在未来函数调用队列的末尾,每个函数调用都将启动一个自己的新调用堆栈。

如何捕获仅在当前调用堆栈展开后才发生的值更改?通过在 之后将另一个异步函数调用排队。以下示例代码说明了与简单超时的区别:

// sync
let count = 0;
function increment() { ++count; }

function invokesCallbackSync(callback) {
    callback();
}

invokesCallbackSync(increment);

console.log(count); // 1

// async
count = 0;

function invokesCallbackAsync(callback) {
    setTimeout(callback);
}

invokesCallbackAsync(increment);

console.log(count); // 0

setTimeout(() => console.log(count)); // 1 (eventually)

promise 的 .then 方法也调用它的异步回调。 then 的好处是它总是返回一个新的 Promise,所以你可以在第一个 Promise 完成后排队另一个异步回调,等等。因此,使您的代码工作的最小更改如下:

let userTracker = {
    counter : 0,
    data : []
}

function fetchUsers(userTracker) {
    let filter  = {"some" : "conditions"}
    return User.find(filter).exec()
    .then( (users ) => {
        return users.forEach((user) => {

            if (userTracker.counter < 20) {
                userTracker.data.push(user)
                userTracker.counter += 1
            } else {
                return 
            }
        })
    })
    .catch(err => {
        console.log(err)
    })
}

fetchUsers(userTracker).then(() => console.log(userTracker))

【讨论】:

    【解决方案2】:

    根据the specificationfind 函数会返回一个Promise,在将要异步执行的所有内容包装在async 函数中之后,您可以await,如下所示:

    let userTracker = {
        counter : 0,
        data : []
    }
    
    async function fetchUsers(userTracker) {
        let filter = {"some" : "conditions"}
        let users = await User.find(filter).exec()
    
        users.forEach((user) => {
            if (userTracker.counter < 20) {
                userTracker.data.push(user)
                userTracker.counter += 1
            } else {
                return 
            }
        })
    }
    
    async function container() {
        await fetchUsers(userTracker)
        console.log(userTracker)
    }
    
    container()
    

    另外快速建议:forEach 在这种情况下不是最好的迭代方法。由于您想在 20 岁之后停止添加用户,因此在 20 岁之后退出 fetchUsers 函数是有意义的。例如,如果有 10000 个用户,则必须不必要地检查所有用户并运行比较,当然是瓶颈。 no way to exit the parent function 来自forEach。也就是说,最好使用传统循环并在计数器达到 20 后调用 return,这将退出 fetchUsers 并停止迭代用户。

    【讨论】:

      猜你喜欢
      • 2020-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-23
      • 2013-05-15
      • 2021-12-03
      • 1970-01-01
      • 2015-08-24
      相关资源
      最近更新 更多