1

I am trying to use a service that runs an asynchronous function. I am trying to call the factory, then do something only it is resolved. But it doesn't work, I get the error : Uncaught TypeError: Cannot read property 'then' of undefined

I am declaring the deferred object into the service function and I return its promise.

Can you help me please ?

app.js:

angular.module('SnowBoard', ['ionic', 'ngCookies', 'ui.unique', 'SnowBoard.controllers', 'SnowBoard.services'])

.run(["isPhoneGap","connectionStatus", 'updateProDB', "$ionicPlatform", '$q', 'sessionService', 'imagesService', function(isPhoneGap, connectionStatus, updateProDB, $ionicPlatform, $q, sessionService, imagesService) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      // org.apache.cordova.statusbar required
      StatusBar.styleDefault();
    }
  });


  var promise = updateProDB.get();
  promise.then(
          function(data) { 
              imagesService.checkIfImagesExistAllAtOnce(prodata);
          },
          function(error) {

          });
}])

service.js:

.service('updateProDB', ['isPhoneGap', 'connectionStatus', 'isIOS', '$q', 'sessionService', function updateProDBFactory(isPhoneGap, connectionStatus, isIOS, $q, sessionService) {
    this.get = function(){

        var debugOptionUseLocalDB=0;
        var prodata=[];
        var gotANewDB;
        var dbReadyDeferred = $q.defer();

        if (typeof debugOptionUseLocalDB != 'undefined' && debugOptionUseLocalDB) {
            fallbackToLocalDBfile();
            return dbReadyDeferred.promise;
            gotANewDB = 1;
            console.log('on a fini fallbackToLocalDBfile, et dbReadyDeferred.state()='+dbReadyDeferred.state());
        }else{
            if(connectionStatus == 'online'){
                console.log("reaching DB on server (getJsonpFile)...");

                getDBfileXHR(dbUrl()).then(function(){ //if resolved
                        console.log(prodata);
                        return dbReadyDeferred.promise;
                    }, function (){ // if rejected
                        console.log("...basic XHR request failed, falling back to local DB file...and sending mail to dev.");
                        fallbackToLocalDBfile();
                        return dbReadyDeferred.promise;
                        var body = ""; 
                        if ( isPhoneGap ) {
                            body += " Platform : phonegap";
                            body += " device.cordova : "+device.cordova;
                            body += " device.model : "+device.model;
                            body += " device.name : "+device.name;
                            body += " device.platform : "+device.platform;
                            body += " device.uuid : "+device.uuid;
                            body += " device.version : "+device.version;
                        } else {
                            body += "Platform : not phonegap -> web browser"
                            body += "navigator.userAgent : "+navigator.userAgent;
                        }
                        var data={
                                userEmail: "louisromain@yahoo.fr",
                                subject: "BoardLine dev issue: had to fallback to local DB file",
                                destEmail: "louisromain@yahoo.fr",
                                body: body           
                               }
                        sendToServer(data).done(funcSuccess).fail(funcError);
                        function funcSuccess(data /* , textStatus, jqXHR */ ) {
                            console.log("Message to dev successfully sent");
                        }
                        function funcError(data , textStatus, jqXHR  ) {
                            console.log("The message to dev could not be sent...");
                        }
                    });


            }else{ //offline
                console.log('device is offline');
                if(localStorage) {
                    if ( isPhoneGap || !isIOS() ) { //BUG iOS safari doesn't work with this (Cf. Philippe's ipad)
                        if (localStorage.getItem("proDB") === null ) { //if proDB exists in localStorage
                            fallbackToLocalDBfile();
                        } else {
                            //popShortToast("...reading DB in localStorage.");
                            var data = JSON.parse(localStorage["proDB"]); //read current localstorage
                            prodata = storeJsonInProdata(data);
                            sessionService.store('prodata', prodata);
                            dbReadyDeferred.resolve(); //initializeSelectButtons();
                            return dbReadyDeferred.promise;
                        }
                    }
                }else{ //if localStorage not available, read local file
                    prodata = fallbackToLocalDBfile();
                    return dbReadyDeferred.promise;
                }
            }
        }

        function getDBfileXHR(url) {
            var getDBfileXHRdeferred = $q.defer();
            var request = new XMLHttpRequest();
            request.open("GET", url, true); //3rd parameter is sync/async
            request.timeout = 2000;
            //console.log(url);
            request.onreadystatechange = function() {      //Call a function when the state changes.
               if (request.readyState == 4) {
                  if (request.status == 200 || request.status == 0) { 
                     console.log('we get a response from XHR');
                     //popShortToast("...updating DB from server using simple XHR.");
                     var jsonText = request.responseText.replace("callback(", "").replace(");", "");
                     prodata = storeJsonInProdata(JSON.parse(jsonText));
                     sessionService.store('prodata', prodata);
                     // console.log(prodata);
                     gotANewDB = 1; 
                     getDBfileXHRdeferred.resolve();
                     dbReadyDeferred.resolve();
                  } else {
                    console.log('error : request.status = '+request.status);
                    getDBfileXHRdeferred.reject();
                  }
               }
            }
            console.log("Sending XMLHttpRequest...");
            request.send();
            return getDBfileXHRdeferred.promise;
        }
        function dbUrl(){
            return unescape(encodeURIComponent("http://user:pass@boardlineapp.com/app/proDB.jsonp")); //JSONP
        }
        function fallbackToLocalDBfile(){
            getDBfileXHR('proDB.jsonp').then(function(){ //if resolved
                        console.log(prodata);
                        return dbReadyDeferred.promise;
                    });
        }
    }

    function sendToServer(dataToSend) {
        return $.ajax({
            url: 'http://aurel:aurel40@boardlineapp.com/app/mail.php',
            type: "POST",
            dataType: "xml",
            data: dataToSend
        });
    }
    function storeJsonInProdata(data) { //function to store the DB json file into a variable prodata usable in the whole app
        console.log("storing json in prodata");
        //clear prodata first
        var prodata=[];
        //if JSON
        var lines=[];
        for(var i = 0; i <= data.length; i++){
            lines[i]=data[i];
        }
        var fieldnames=lines[0];
        //if tab separated TXT with each model on a separate line
        //      var lines=data.split(/\n/);
        //      var fieldnames=lines[0].split(/\t/);
        var i;
        prodata.push(lines[0]);
        //prodata.push(0);
        for (i = 1; i < lines.length-1; ++i) {
            //if JSON
            var fields=lines[i];
            //if TXT            
            //          var fields=lines[i].split(/\t/);
            //prodata.push(i);          
            var j;
            prodata[i]={};
            prodata[i]['id']=i; //auto id, there is no more 'id' column in the DB file.
            for (j = 0; j < fields.length; ++j) {
                var str=fieldnames[j];
                prodata[i][str]=fields[j];  
            }
        }
        return prodata;
    }

}]);
4

2 回答 2

1

There appears to be at least one path that doesn't not return a value, nutshell is the path where:

   !debugOptionUseLocalBD 
&& connectionStats != 'online' 
&& localStorage 
&& (isPhoneGap || !sIOS()) 
&& localStorage.getItem("proDB") === null

The if (connectionStatus == 'online') also appears to not return anything immediately.

I find the code a bit difficult to follow–the above may not be completely accurate.

That branch only calls fallbackToLocalDBfile(), and there appears to be no return. (I think.)

if (typeof debugOptionUseLocalDB != 'undefined' && debugOptionUseLocalDB) {
  fallbackToLocalDBfile();
  gotANewDB = 1;
  return dbReadyDeferred.promise;
} else {
  if (connectionStatus == 'online') {
    //
    // !!!!!!!!!! HERE !!!!!!!!!!
    //
    getDBfileXHR(dbUrl()).then(function () {
      console.log(prodata);
      return dbReadyDeferred.promise;
    }, function () {
      fallbackToLocalDBfile();

      // The isPhoneGap/etc. and sendToServer() call; elided for clarity
      sendDevMessage();

      return dbReadyDeferred.promise;
    });
  } else {
    if (localStorage) {
      if (isPhoneGap || !isIOS()) { //BUG iOS safari doesn't work with this (Cf. Philippe's ipad)
        if (localStorage.getItem("proDB") === null) {
          //
          // !!!!!!!!!! HERE !!!!!!!!!!
          //
          fallbackToLocalDBfile();
        } else {
          var data = JSON.parse(localStorage["proDB"]);
          prodata = storeJsonInProdata(data);
          sessionService.store('prodata', prodata);
          dbReadyDeferred.resolve();
          return dbReadyDeferred.promise;
        }
      }
    } else {
      prodata = fallbackToLocalDBfile();
      return dbReadyDeferred.promise;
    }
  }
}

Not explaining the circumstances under which the error occurs makes it difficult to help further, but it appears there's at least one set of conditions under which you get undefined back, and it may be the one I'm highlighting here.

Again, this is more or less a stab in the dark. The code is difficult to understand, as it mixes AngularJS, jQuery (AFAICT, re: the $.ajax call), raw XMLHttpRequests, etc. There are a significant number of issues present that make reasoning about what's happening quite difficult, at least for me.

于 2014-12-01T15:00:20.717 回答
0

路易斯,这不仅仅是确保从所有可能的分支返回一些东西。

您必须确切地知道从每个内部函数返回什么,这样.get()最终将返回一个prodata无论派生的承诺。

整个事情可以而且应该在不创建明确的承诺dbReadyDeferredgetDBfileXHRdeferred. 它们是不必要的,许多return dbReadyDeferred.promise陈述都是错误的。

jQuery.ajax()在棱角分明的环境中使用会使生活变得有些尴尬。“正确”的解决方案是使用jQuery 承诺来$http()代替,jQuery.ajax()但可以通过将 jQuery 承诺强制为 $q 来实现(或至少尝试)很多事情,从而保证.get()总是返回一个角度承诺。

这个经过大量修改的服务版本未经测试,可能仍需要调试,但应该让您对所需内容有一个很好的了解。

.service('updateProDB', ['isPhoneGap', 'connectionStatus', 'isIOS', '$q', 'sessionService', function updateProDBFactory(isPhoneGap, connectionStatus, isIOS, $q, sessionService) {
    this.get = function() {
        var debugOptionUseLocalDB = 0;
        if(typeof debugOptionUseLocalDB != 'undefined' && debugOptionUseLocalDB) {
            return fallbackToLocalDBfile();
        } else {
            if(connectionStatus == 'online') {
                console.log("reaching DB on server (getJsonpFile)...");
                return getDBfileXHR(dbUrl()).then(null, function () {
                    console.log("...basic XHR request failed, falling back to local DB file...and sending mail to dev.");
                    return fallbackToLocalDBfile().then(function(prodata) {
                        var body = [];
                        if (isPhoneGap) {
                            body.push("Platform : phonegap");
                            body.push("device.cordova : " + device.cordova);
                            body.push("device.model : " + device.model);
                            body.push("device.name : " + device.name);
                            body.push("device.platform : " + device.platform);
                            body.push("device.uuid : " + device.uuid);
                            body.push("device.version : " + device.version);
                        } else {
                            body.push("Platform : not phonegap -> web browser");
                            body.push("navigator.userAgent : " + navigator.userAgent);
                        }
                        var data = {
                            userEmail: "louisromain@yahoo.fr",
                            subject: "BoardLine dev issue: had to fallback to local DB file",
                            destEmail: "louisromain@yahoo.fr",
                            body: body.join(' ')
                        };
                        return sendToServer(data).then(function() {
                            return prodata;
                        }, function(textStatus) {
                            console.error("The message to dev could not be sent... :" + textStatus);
                            return textStatus;
                        });
                    });
                });
            } else { //offline
                console.log('device is offline');
                if(localStorage) {
                    if (isPhoneGap || !isIOS()) { //BUG iOS safari doesn't work with this (Cf. Philippe's ipad)
                        if (localStorage.getItem("proDB") === null ) { //if proDB doesn't exist in localStorage
                            return fallbackToLocalDBfile();
                        } else {
                            var data = JSON.parse(localStorage["proDB"]); //read current localstorage
                            var prodata = storeJsonInProdata(data);
                            sessionService.store('prodata', prodata);
                            return $q.when(prodata); //promise of prodata
                        }
                    } else {
                        return fallbackToLocalDBfile();
                    }
                } else { //if localStorage not available, read local file
                    return fallbackToLocalDBfile();
                }
            }
        }
        function getDBfileXHR(url) {
            console.log("Sending XMLHttpRequest...");
            var jqXHR = $.ajax({
                url: url,
                dataType: 'json',
                timeout: 2000
            }).then(function(data) {
                var prodata = storeJsonInProdata(data);
                sessionService.store('prodata', prodata);
                return prodata;
            }, function(jqXHR, textstatus, errorThrown) {
                console.error('getDBfileXHR: url=' + url + ': ' + textstatus);
                return textstatus;
            });
            return $q.when(jqXHR);//coerce jQuery promise to angular
        }
        function dbUrl() {
            return unescape(encodeURIComponent("http://user:pass@boardlineapp.com/app/proDB.jsonp")); //JSONP
        }
        function fallbackToLocalDBfile() {
            return getDBfileXHR('proDB.jsonp');
        }
    }

    function sendToServer(dataToSend) {
        var jqXHR = $.ajax({
            url: 'http://aurel:aurel40@boardlineapp.com/app/mail.php',
            type: "POST",
            dataType: "xml",
            data: dataToSend
        }).then(function(data, textStatus, jqXHR) {
            return data;
        }, function(jqXHR, textStatus, errorThrown) {
            console.error('sendToServer: dataToSend=' + dataToSend + ': ' + textstatus);
            return textStatus;
        });
        return $q.when(jqXHR);//coerce jQuery promise to angular
    }
    function storeJsonInProdata(lines) { //function to store the DB json file into a variable prodata usable in the whole app
        var i, j;
        console.log("storing json in prodata");
        var prodata = [lines[0]];
        for (i = 1; i < lines.length-1; ++i) {
            prodata[i] = { 'id': i }; //auto id, there is no more 'id' column in the DB file.
            for (j = 0; j < lines[i].length; ++j) {
                prodata[i][lines[0][j]] = lines[i][j];
            }
        }
        return prodata;
    }
}]);

我坚持使用与问题中的代码相同的整体结构,但是您可以(并且可能应该)通过在fallbackToLocalDBfile(prodata)调用的每个点抛出一个错误并在通用的catch. 那会更干净。

我也尝试整理storeJsonInProdata(),这需要测试。

于 2014-12-02T08:08:59.783 回答