簡體   English   中英

為什么 Firebase 會在 once() 函數之外丟失引用?

[英]Why Does Firebase Lose Reference outside the once() Function?

我正在使用 Firebase 和 angularJS 來獲取用戶列表。 我可以使用once()函數從我的數據庫中讀取所有用戶,但我不知道為什么userList在下面返回 undefined

.service('userService', [function() {
    this.getUsers = function() {
        var users;
        var userList;
        var ref = firebase.database().ref('/users/');
        ref.once('value').then(function(snapshot) {
            users = snapshot.val();
            for(var key in users) {
                users[key].id = key;
                // do some other stuff
            }
            console.log(users); // outputs all users
        }).then(function(){
            userList = users; 
            console.log(userList); // outputs all users
        },(function(error){
            alert('error:  ' + error);
        });
        console.log(userList); // outputs 'undefined'
    }
}]);

我等待分配我的userList變量,直到我完成處理users ,但沒有運氣。

這里有一些重要的東西,我在 Promises/callbacks 方面遺漏了,我在文檔中找不到它; 有人可以幫我理解我的問題嗎?

問題是(正如 imjared 所說)數據是從 Firebase 異步讀取的。 所以代碼不會按照你想象的順序執行。 通過僅使用一些日志語句對其進行簡化,最容易看出這一點:

.service('userService', [function() {
    this.getUsers = function() {
        var ref = firebase.database().ref('/users/');
        console.log("before attaching listener");
        ref.once('value').then(function(snapshot) {
            console.log("got value");
        });
        console.log("after attaching listener");
    }
}]);

其輸出將是:

在附加監聽器之前

附加監聽器后

得到了價值

知道它的執行順序應該可以完美地解釋為什么在您剛剛附加監聽器后無法打印用戶列表。 如果沒有,我建議您也閱讀這個很好的答案: 如何從異步調用中返回響應

現在解決方案:您需要在回調中使用用戶列表返回一個承諾。

在回調中使用用戶列表

這是處理異步代碼最古老的方法:將所有需要用戶列表的代碼移到回調中。

    ref.once('value', function(snapshot) {
        users = snapshot.val();
        for(var key in users) {
            users[key].id = key;
        }
        console.log(users); // outputs all users
    })

您將代碼從“首先加載用戶列表,然后打印其內容”重新構建為“每當加載用戶列表時,打印其內容”。 定義上的差異很小,但突然之間你就可以完美地處理異步加載了。

你也可以對 Promise 做同樣的事情,就像你在代碼中做的那樣:

   ref.once('value').then(function(snapshot) {
        users = snapshot.val();
        for(var key in users) {
            users[key].id = key;
            // do some other stuff
        }
        console.log(users); // outputs all users
    });

但是使用 promise 比直接使用回調有一個巨大的優勢:你可以返回一個 promise

返回一個承諾

通常,您不希望將需要用戶的所有代碼都放入getUsers()函數中。 在這種情況下,您可以將回調傳遞給getUsers() (我不會在這里展示,但它與您可以傳遞給once()的回調非常相似) ,或者您可以從getUsers()返回一個承諾:

this.getUsers = function() {
    var ref = firebase.database().ref('/users/');
    return ref.once('value').then(function(snapshot) {
        users = snapshot.val();
        for(var key in users) {
            users[key].id = key;
            // do some other stuff
        }
        return(users);
    }).catch(function(error){
        alert('error:  ' + error);
    });
}

使用此服務,我們現在可以調用getUsers()並使用生成的承諾在用戶加載后獲取用戶:

userService.getUsers().then(function(userList) {
    console.log(userList);
})

這樣你就馴服了異步野獸。 嗯....至少現在。 即使是經驗豐富的 JavaScript 開發人員偶爾也會感到困惑,所以如果需要一些時間來適應,請不要擔心。

使用異步和等待

現在該函數返回了一個 Promise,您可以使用async / await使上面的最終調用看起來更熟悉一些:

function getAndLogUsers() async {
    const userList = await userService.getUsers();
    console.log(userList);
}

由於使用了await關鍵字,您可以看到這段代碼看起來幾乎像一個同步調用。 但是為了能夠使用它,我們必須將getAndLogUsers (或我們使用await的任何父范圍)標記為async ,這意味着它也返回一個Future 所以任何調用getAndLogUsers的人都需要注意它的異步特性。

你需要考慮異步:

.service('userService', [function() {
    this.getUsers = function() {
        var users;
        var ref = firebase.database().ref('/users/');

        <!-- this happens ASYNC -->
        ref.once('value', function(snapshot) {
            users = snapshot.val();
            for(var key in users) {
                users[key].id = key;
                // do some other stuff
            }
            console.log(users); // outputs all users
        },function(error){
            alert('error:  ' + error);
        });
        <!-- end async action -->

        console.log(users); // outputs 'undefined'
    }
}]);

我敢打賭,如果你這樣做,你會沒事的:

.service('userService', [function() {
    this.getUsers = function() {
        var users;
        var ref = firebase.database().ref('/users/');

        <!-- this happens ASYNC -->
        ref.once('value', function(snapshot) {
            users = snapshot.val();
            for(var key in users) {
                users[key].id = key;
                // do some other stuff
            }
            console.log(users); // outputs all users
        },function(error){
            alert('error:  ' + error);
        });
        <!-- end async action -->

        window.setTimeout( function() {
            console.log(users); // outputs 'undefined'
        }, 1500 );
    }
}]);

根據弗蘭克斯的評論澄清:

我應該進一步澄清setTimeout只是證明這是一個時間問題。 如果您在應用程序中使用 setTimeout,您可能會遇到很糟糕的情況,因為您無法可靠地等待 n 毫秒以獲得結果,您需要獲取它們,然后在獲得快照后觸發回調或解決承諾數據。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM