在维护数据的同时链接 Promise(角度 js)

Posted

技术标签:

【中文标题】在维护数据的同时链接 Promise(角度 js)【英文标题】:Chaining Promises while maintaining data (angular js) 【发布时间】:2016-07-14 13:36:16 【问题描述】:

我看到有关于链式 Promise 的问题,但这个有点不同。 我在我的代码中发出 http get 请求。第一次调用返回一个数组。对于数组中的每个对象,我需要进行另一个 http 调用,该调用返回另一个数组,依此类推(这个链接 3 层深)。 问题是,我需要跟踪用于进行每个 http 调用的数组元素,而我不知道如何使用 Promise 来做到这一点。 我还想通过返回一个承诺来结束这个链条。

我有我想做的事用 nodejs 编写的代码,没有承诺:

var https = require('https');
var fs = require('fs');


function makeRequest(options)
  var httpopts = 
    host: 'soc.courseoff.com',
    path: '/gatech/terms/201601/majors/' + options.p,
    method: 'GET'
  ;
  var response = "";
  var req = https.request(httpopts, function(res) 
      res.on('data', function(d) 
        response += d;
      );
      res.on('end',function()
        options.cb(response,options)
      )
    );

  req.end();

  req.on('error', function(e) 
    console.error(e);
  );

var classData = ;
function getCourses(m)
    var majors = JSON.parse(m);
    majors.forEach(function(maj)
        classData[maj] = ;
        var options = 
           p:maj.ident +'/courses',
           cb:getSections,
           major:maj
        ;    
        makeRequest(options);
    );

var classCount = 0;
function getSections(c,opts)
    var courses = JSON.parse(c);
    courses.forEach(function(course) 
        classCount++;
        var options = JSON.parse(JSON.stringify(opts)); 
        options.p += '/'+course.ident+'/sections';
        options.course = course
        options.cb = buildData
        makeRequest(options)
    );

var sectionCount = 0;
function buildData(r, options)
    var major = options.major.ident;
    sectionCount++;
    if(!classData[major])
        classData[major] = 
            name: options.major.name,
            classes:
        ;
    
    classData[major].classes[options.course.ident] = 
        name:options.course.name,
        sections:JSON.parse(r)
    ;
    console.log('classCount-sectionCount '+classCount + '---'+sectionCount);
    if(classCount === sectionCount)
        writeIt();
    

makeRequest(
    p:'',
    cb:getCourses
);

function writeIt()
    fs.writeFileSync('./classData.js', 'module.exports = ' + JSON.stringify(classData));

编辑: 我设法在跟踪数据的同时获得嵌套的承诺,但我怎样才能返回最终用最终数据对象解决的承诺? 我的代码: 谢谢四爷的帮助!我已经设法对其进行了编码,以便承诺有效,我现在唯一的问题是将最终数据作为承诺返回

fact.factory('ClassFactory', ['$http',function ($http)     
    var eventData = ;
        var promise;
        var courseData = [];
        var baseURL ='https://soc.courseoff.com/gatech/terms/201601/majors/';
        eventData.getClasses = function (event) 
            if(!promise)
                 promise = $http.get(baseURL).then(
                    function(majors)
                        Promise.all(majors.data.map(m => $http.get(baseURL + m.ident+'/courses')
                            .then(
                                function(courses)
                                    if(!m.courses) m.courses = [];
                                    courses.data.map(c => $http.get(baseURL+ m.ident+'/courses/' +c.ident+'/sections' )
                                        .then(
                                            function(sections)
                                                c.sections = sections.data;
                                                m.courses.push(c);
                                            
                                        ));
                                    courseData.push(m);
                                
                            )));
                    
                 )
            
            return promise;
        
        return eventData;
]);

【问题讨论】:

【参考方案1】:

几乎可以肯定,每次处理一系列 Promise 时,您都需要使用 Promise.all 来连接您的 Promise 并将其合并为一个新的 Promise。然后,该承诺将包含每个调用的结果数组。因此,嵌套的 Promise.alls 可以返回包含所有级别结果的 Arrays of Arrays,只要您使用地图和闭包之类的东西来捕获外部级别。

var fakeCall = x => Promise.resolve(x||Math.random());

Promise.all([fakeCall(1),fakeCall(2)])
  .then( 
    results => Promise.all(results.map( x => fakeCall(5).then( results2 => [x, results2])  ))
  )
  .then( x => console.log(x));//-> [[1,5],[2,5]]

第一个调用数组会生成一个结果数组,使用进行更多调用的函数对这些结果进行映射将返回一个可以与其父对象配对的结果。

以这种方式显式嵌套内容将适用于更深层次,但不会很漂亮。您可能可以使用 Array.reduce 创建一个抽象来概括这种模式。

【讨论】:

感谢您的帮助!我仍然无法将其作为承诺返回,我编辑了原始问题 我认为您仍然需要返回 Promise.all 本身。正如其他评论者所说,传递给 then 的所有函数都必须返回一个值或另一个 Promise:否则您返回的所有内容都是未定义的。【参考方案2】:

您在代码中忘记了一些返回值。你传递给.then 的函数应该总是返回一些东西。此外,您正在修改majors,但随后将其丢弃而不使用它。在使用 Promise 时——尤其是当它们复杂且嵌套时——除非你确定不会发生任何不好的事情,否则修改这些 Promise 中包含的任何数据结构都不是一个好主意。

我会把它分成几个功能。 例如

var baseURL ='https://soc.courseoff.com/gatech/terms/201601/majors/';

function getSections(major, course) 
  return $http.get(baseURL+ major.ident+'/courses/' +course.ident+'/sections')
              .then(sections => sections.data)
              .catch(e => []);


function getCourses(major) 
  return $http.get(baseURL + major.ident+'/courses')
              .then(courses => Promise.all(courses.data.map(course =>
                getSections(major, course).then(sections => ([course.ident]: name: course.name, sections: sections)))))
              .then(courses => angular.extend(, ...courses))
              .catch(e => ());


function getClassData() 
  return $http.get(baseURL)
              .then(majors => Promise.all(majors.data.map(major =>
                getCourses(major).then(courses => ([major.ident]: name: major.name, classes: courses)))))
              .then(majors => angular.extend(, ...majors))
              .catch(e => ());


getClassData().then(data => console.log(data));

【讨论】:

以上是关于在维护数据的同时链接 Promise(角度 js)的主要内容,如果未能解决你的问题,请参考以下文章

js异步解决方案及promise基础

链接 redux-actions 和 redux-promise-middleware

[js高手之路] es6系列教程 - promise常见用法详解(resolve,reject,catch,then,all,race)

在 JS 中使用 Promise 链的正确方法

如何在角度4中创建路由延迟时间?

promise封装小程序api请求