JavaScript如果已经使用了promise,如何克服JavaScript的异步性?

时间:2017-08-28 13:38:38

标签: javascript angularjs asynchronous promise angular-promise

这不是我第一次遇到这个问题,但我总是设法找到一个解决方法,我想这很好,但现在我想了解一下如何改进。所以,在我的例子中,我有一个课程列表,我需要计算每个课程的平均评分:

var vm = this;

course.averageRating = vm.getAverageRating(course.id);

现在,什么行不通:

vm.getAverageRating = function (courseId) {
       let sum = 0, courses = [];
       courseContext.getUserCourseFeedback(courseId).then(function(results){
            courses = results;
        });

       courses.forEach(function (course) {
            if(course && course.feedback_rating) {
                sum += course.feedback_rating;
            }
       });
       return courses.length > 0 ? sum / courses.length : null;
};

显然,在应用程序实际从服务器下载userCourses列表之前, courses.forEach 会触发,并且函数始终返回 null

我的解决方法如下:

vm.averageRatings = {};

vm.getAverageRating = function (courseId) {
  let sum = 0, courses = [];
  courseContext.getUserCourseFeedback(courseId).then(function(results){
       courses = results;

       courses.forEach(function (course) {
            if(course && course.feedback_rating) {
                sum += course.feedback_rating;
            }
       });

       vm.averageRatings[courseId] = courses.length > 0 ? sum/courses.length : null;
  });
};

我认为这不是解决这个问题的最佳方法。我在第一个例子中不能在然后中使用return语句,而且我也不能在promise上使用promise。如果你在我的位置,你会如何解决这个问题?

5 个答案:

答案 0 :(得分:1)

getAverageRating 异步的。但是,您可能希望缓存结果:

 var cache = {};   
 vm.getAverageRating = function (courseId) {

  return cache[courseId] || 
   (cache[courseId] = courseContext.getUserCourseFeedback(courseId)
     .then(function(courses){
        return (
         courses.reduce((sum,course)=>sum + (course.feedback_rating || 0),0)
         / courses.length) || null;
    })
  );      
};

可以这样:

vm.getAverageRating(1).then( res => console.log(res));
vm.getAverageRating(1).then( res => console.log(res));

两者都将引用相同的承诺,因此它们将同时以相同的结果得到解决。没有两个请求,只有一个。

答案 1 :(得分:1)

回答问题

  

如果你在我的位置,你会如何解决这个问题?

两种方法的组合将是

vm.getAverageRating = function (courseId) {
 return courseContext.getUserCourseFeedback(courseId).then(courses => courses.length > 0 ? (courses.map(course => (course && course.feedback_rating) ? course.feedback_rating : 0).reduce((a, b) => a + b))/courses.length : null);
};

使用方法:

vm.getAverageRating("someID").then(result => {
    // result is the result you want
});
  

第一段代码很简单,如果ES2015 +不熟悉

vm.getAverageRating = function (courseId) {
    return courseContext.getUserCourseFeedback(courseId).then(function (courses) {
        return courses.length > 0 ? courses.map(function (course) {
            return (course && course.feedback_rating) ? course.feedback_rating : 0;
        }).reduce(function (a, b) {
            return a + b;
        }) / courses.length : null;
    });
};

答案 2 :(得分:1)

使用承诺很好,但你应该

  • 返回值的承诺,而不是写入某些预先确定的对象
  • 简化您的代码,特别是变量声明

function getAverageRating(courseId) {
  return courseContext.getUserCourseFeedback(courseId).then(function(courses) {
//^^^^^^                                                ^^^^^^^
    let sum = 0;
    for (const course of courses)
      sum += course && course.feedback_rating || 0;
    return courses.length > 0 ? sum/courses.length : null;
  });
};

getAverageRating(course.id).then(avg => {
  course.averageRating = avg;
});

答案 3 :(得分:0)

如果您使用的是babel或Node 8.x,则可以使用async / await

这会简化你的功能:

vm.getAverageRating = async function (courseId) {
       let sum = 0, courses = [];
       await courseContext.getUserCourseFeedback(courseId).then(function(results){
            courses = results;
        });

       courses.forEach(function (course) {
            if(course && course.feedback_rating) {
                sum += course.feedback_rating;
            }
       });
       return courses.length > 0 ? sum / courses.length : null;
};

答案 4 :(得分:0)

vm.getAverageRating()依赖于异步操作的结果,因此它不会向同步时间轴返回任何内容。你所做的基本上是正确的,但如果vm.getAverageRating()返回一个承诺会更好。所以我会模仿你的代码如下;

function getAverageRating(n){
  return Promise.resolve(Array.from({length: n})
                              .map(_ => 25+Math.floor(76*Math.random()))) // get course ratings
                .then(function(crs){
                        var sum = crs.reduce((p,c) => p+c),
                            avg = crs.length ? sum / crs.length : null;
                        return [crs,avg];
                      });
}
getAverageRating(10).then(([crs,avg]) => console.log("course ratings are: ", crs, "average is: ", avg));