Изменить и вернуть объект внутри функции asyn c - PullRequest
1 голос
/ 17 апреля 2020

Я хочу изменить объект (userTracker) внутри асинхронного js кода. После запуска функции (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`

Как получить доступ к заполненному объекту userTracker после запуска функции fetchUsers? Я хочу закончить с чем-то вроде этого

Чего я хочу достичь:

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

Я отстой в асинхронном c программировании, любая помощь по достижению желаемого результата выше с благодарностью.

Ответы [ 2 ]

1 голос
/ 17 апреля 2020

Ответ @ shivashrigane sh -mahato правильный, но я хотел бы объяснить, почему ваш исходный код не работает.

Всякий раз, когда вы передаете обратный вызов функция, эта функция может вызывать ее одним из двух способов: (1) syn c или (2) asyn c. Син c легко понять; это означает, что обратный вызов выполняется во время выполнения функции, которой вы передали обратный вызов.

Asyn c очень отличается. Выполнение обратного вызова откладывается до тех пор, пока не будет завершен весь код syn c, который выполняется в данный момент. Это включает в себя функцию, которой вы передали обратный вызов, функцию, которая включает в себя строку, в которой вы вызываете эту функцию, и так далее. Обратный вызов откладывается по крайней мере до тех пор, пока не завершится выполнение всего текущего стека вызовов, и, возможно, еще дольше после этого. Например, когда вы отправляете сетевой запрос и передаете обратный вызов для обработки ответа, обратный вызов не только откладывается до тех пор, пока не очищается стек вызовов, но и до тех пор, пока не будет получен ответ. С точки зрения компьютера это может занять вечность (т. Е. Несколько миллисекунд).

Asyn c вызовы функций не помещаются поверх стека вызовов. Вместо этого они помещаются в конец очереди будущих вызовов функций, каждый из которых запускает новый собственный стек вызовов.

Как отловить изменение значения, которое происходит только после текущего стека вызовов раскручивается? В очереди другой асин c вызов функции после it. Следующий пример кода иллюстрирует разницу с простыми тайм-аутами:

// 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)

Метод обещания .then также вызывает его асинхронный обратный вызов c. Приятной особенностью then является то, что он всегда возвращает новое обещание, поэтому вы можете поставить в очередь еще один асинхронный обратный вызов c после завершения первого обещания и так далее. Таким образом, минимальное изменение для обеспечения работы вашего кода выглядит следующим образом:

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))
1 голос
/ 17 апреля 2020

Per спецификация , функция find возвращает Promise, который вы можете await после обертывания всего, что вы хотите выполнить асинхронно в async функциях, например:

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, имеет смысл просто выйти из функции fetchUsers после 20. Например, если есть 10000 пользователей, ей придется без необходимости go пройти через всех из них и выполнить сравнение, конечно узкое место. И у нет способа выйти из родительской функции изнутри forEach. Тем не менее, лучше использовать традиционный l oop и вызывать return после того, как счетчик достигнет 20, что выйдет из fetchUsers и прекратит итерацию пользователей.

...