AngularJS工厂调用Parse Server报错:Cannot read property 'then' of undefined
这个错误TypeError: Cannot read property 'then' of undefined的核心原因很明确:你的ProductsFactory.getProducts()函数没有返回一个Promise对象——你把return product_dfd.promise写在了qr.get()的success回调函数内部,而getProducts本身执行完异步的qr.get()后就直接返回了undefined,自然没法调用.then()方法。
问题代码的关键缺陷
当你调用ProductsFactory.getProducts()时,函数会立即执行qr.get()(这是一个异步操作,不会等待回调执行),然后函数本身没有任何返回值,默认返回undefined。你写的return product_dfd.promise是在回调函数里的,只有当qr.get()成功后才会执行,但这时候getProducts早就执行完了,返回值已经是undefined了。
修正后的工厂代码
我们需要让getProducts直接返回一个Promise,把整个异步逻辑包裹在Promise的处理流程中,这里用AngularJS的$q来实现:
sebetimapp.factory('ProductsFactory', ['$q', function($q){ Parse.initialize('MY_APP_ID', 'JS_KEY'); Parse.serverURL = 'https://parseapi.back4app.com/'; let fac = {}; fac.getProducts = function(cat, lmt) { // 直接返回由$q创建的Promise return $q(function(resolve, reject) { let Category = Parse.Object.extend('Category'), qr = new Parse.Query(Category); qr.get(cat, { success: function (categoryRes) { // 类别查询成功后,执行产品查询 let Product = Parse.Object.extend('Product'), query = new Parse.Query(Product); query.include('category'); query.equalTo('category', categoryRes); if (lmt) { query.limit(lmt); } query.find({ success: function(productResults) { resolve(productResults); // 解析产品查询结果 }, error: function(productErr) { reject(productErr); // 传递产品查询错误 } }); }, error: function(object, categoryErr) { reject(categoryErr); // 传递类别查询错误 } }); }); }; return fac; }]);
更进一步的简化(利用Parse的Promise支持)
其实Parse Server的查询方法本身是支持返回Promise的,我们可以用$q.when()把Parse的Promise转换成AngularJS的Promise(确保回调在Angular的digest cycle中执行,避免视图不更新的问题),代码会更简洁:
sebetimapp.factory('ProductsFactory', ['$q', function($q){ Parse.initialize('MY_APP_ID', 'JS_KEY'); Parse.serverURL = 'https://parseapi.back4app.com/'; let fac = {}; fac.getProducts = function(cat, lmt) { let Category = Parse.Object.extend('Category'), qr = new Parse.Query(Category); // 链式处理Promise:先查类别,再查对应产品 return $q.when(qr.get(cat)) .then(function(categoryRes) { let Product = Parse.Object.extend('Product'), query = new Parse.Query(Product); query.include('category'); query.equalTo('category', categoryRes); if (lmt) { query.limit(lmt); } return $q.when(query.find()); // 返回产品查询的Promise }) .catch(function(err) { // 统一处理所有错误 console.error('查询失败:', err); throw err; // 继续向上抛出错误,让控制器捕获 }); }; return fac; }]);
控制器代码无需修改
你的控制器代码是正确的,只要工厂方法返回了Promise,.then()就能正常执行:
sebetimapp.controller('productsCtrl', ['$scope', '$log', '$location', '$q', 'ProductsFactory', function($scope, $log, $location, $q, ProductsFactory) { let params = $location.search(); // 从URL获取类别ID ProductsFactory.getProducts(params.cat, 20).then(function(response) { $log.log('Successfully retrieved products.'); $scope.products = response; // 记得把结果赋值给scope,更新视图 }, function(error) { $log.log('Unable to get products.', error); }); }]);
总结
核心问题就是工厂方法没有返回Promise,把return语句写在了异步回调内部导致函数返回undefined。只要让getProducts直接返回一个Promise对象,就能正常使用.then()进行链式调用了。
内容的提问来源于stack exchange,提问作者N'Bayramberdiyev

