如何构造回调来处理来自API调用的异步返回

时间:2017-12-01 21:13:14

标签: javascript asynchronous

我有以下代码:

import GtfsRealtimeBindings from 'mta-gtfs-realtime-bindings';
import rp from 'request-promise';

const GetFeedData = (function () {
    let feed, feedId;
    return {
        getFeedId: function (sub) {
            switch (sub) {
                case '1': case '2': case '3': case '4': case '5': case '6': case 'S':
                    feedId = 1;
                    break;
                case 'A': case 'C': case 'E':
                    feedId = 26;
                    break;
                case 'N': case 'Q': case 'R': case 'W':
                    feedId = 16;
                    break;
                case 'B': case 'D': case 'F': case 'M':
                    feedId = 21;
                    break;
                case 'L':
                    feedId = 2;
                    break;
                case 'G':
                    feedId = 31;
                    break;
            }
        },
        getFeedData: function () {
            rp({
                method: 'GET',
                url: 'https://cors-anywhere.herokuapp.com/http://datamine.mta.info/mta_esi.php?key=MY_KEY&feed_id=' + feedId,
                encoding: null
            }).then((buf) => {
                feed = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(buf);
            });
        },
        get feed () { return feed; }
    };
})();

const ReverseStop = (function () {
    let stopIdN, stopIdS;
    const stopData = require('./stops');
    return {
        reverseStop: function (sub, stop) {
            function filterByName (item) {
                if (item.stop_name == stop && typeof item.stop_id === 'string' && item.stop_id.charAt(0) == sub) {
                    return true;
                }
                return false;
            }
            var stopObjs = stopData.filter(filterByName);
            for (var i = 0; i < stopObjs.length; i++) {
                if (stopObjs[i].stop_id.charAt(stopObjs[i].stop_id.length - 1) == 'N') {
                    stopIdN = stopObjs[i].stop_id;
                } else if (stopObjs[i].stop_id.charAt(stopObjs[i].stop_id.length - 1) == 'S') {
                    stopIdS = stopObjs[i].stop_id;
                }
            }
        },
        stopIdN: stopIdN,
        stopIdS: stopIdS
    };
})();

export const IsDelayN = (function () {
    let noDelay, yesDelay;
    return {
        isDelay: function (sub, stop) {
            GetFeedData.getFeedId(sub);
            GetFeedData.getFeedData();
            ReverseStop.reverseStop(sub, stop);
            var arrivals = [];
            var delays = [];
            (function dataFilter () {
                var feedObjs = GetFeedData.feed().entity.filter(function (entityObj) {
                    if (entityObj.trip_update !== null && entityObj.trip_update.stop_time_update.stop_id == ReverseStop.stopIdN) {
                        return entityObj.trip_update.stop_time_update;
                    }
                });
                for (var i = 0; i < feedObjs.length; i++) {
                    if (feedObjs.arrival !== undefined) {
                        arrivals.push(feedObjs.arrival.time.low);
                        delays.push(feedObjs.arrival.delay);
                    }
                }
            })();
            var nextArrival = Math.min(...arrivals);
            var delayIndex = arrivals.indexOf(nextArrival);
            var delay = delays.delayIndex;
            if (delay === null || Math.ceil(delay / 60) <= 5) {
                noDelay = Math.ceil((nextArrival - GetFeedData.feed.header.timestamp.low) / 60);
            } else {
                yesDelay = Math.ceil(delay / 60);
            }
        },
        noDelay: noDelay,
        yesDelay: yesDelay,
    };
})();

export const IsDelayS = (function () {
    let noDelay, yesDelay;
    return {
        isDelay: function (sub, stop) {
            GetFeedData.getFeedId(sub);
            GetFeedData.getFeedData();
            ReverseStop.reverseStop(sub, stop);
            var arrivals = [];
            var delays = [];
            (function dataFilter () {
                var feedObjs = GetFeedData.feed().entity.filter(function (entityObj) {
                    if (entityObj.trip_update !== null && entityObj.trip_update.stop_time_update.stop_id == ReverseStop.stopIdS) {
                        return entityObj.trip_update.stop_time_update;
                    }
                    return false;
                });
                for (var i = 0; i < feedObjs.length; i++) {
                    if (feedObjs.arrival !== undefined) {
                        arrivals.push(feedObjs.arrival.time.low);
                        delays.push(feedObjs.arrival.delay);
                    }
                }
            })();
            var nextArrival = Math.min(...arrivals);
            var delayIndex = arrivals.indexOf(nextArrival);
            var delay = delays.delayIndex;
            if (delay === null || Math.ceil(delay / 60) <= 5) {
                noDelay = Math.ceil((nextArrival - GetFeedData.feed.header.timestamp.low) / 60);
            } else {
                yesDelay = Math.ceil(delay / 60);
            }
        },
        noDelay: noDelay,
        yesDelay: yesDelay,
    };
})();
位于脚本顶部的IIFE末尾的

feed会返回undefined,因为request-promise中的getFeedData调用是异步的,feed会返回IIFE顶部的变量在更新之前。我认为我需要做的是将GetFeedData.feed更改为某种回调(或返回一个承诺)。我不确定该回调应该是什么样的,特别是因为我不认为我可以在该回调中调用getFeedData,因为这意味着尝试访问同一对象中的对象属性(我非常肯定是禁止的。)

另一件事是,有人告诉我,我需要更改使用 feedback的代码。这将是dataFilter IIFE IsDelayNIsDelayS的一部分。我不确定我将如何更改该代码。

最后,我得到的错误消息是TypeError: Cannot read property 'filter' of undefined。我假设entity未定义,因为feed由于上述问题而成为空变量。有人能证实吗?如果是这样,有人能帮我解决吗?

const GetFeedData = (function () {
    let feed, feedId;
    return {
        getFeedId: function () {
            // All this does is take input from one of my React components
            // and return the feed_id for the end of the url in the API call
        },
        getFeedData:
            // This is the API call itself. It modifies the feed variable above but
            // is asynchronous. The feed getter below is what's returning an unmodified
            // variable,
        get feed() { return feed; }
    };
})();

const ReverseStop = (function () {
    // This does some other stuff that I don't think factors into my question.
})();

export const IsDelayN = (function () {
    let noDelay, yesDelay;
    return {
        isDelay: function (sub, stop) {
      GetFeedData.getFeedId(sub);
      GetFeedData.getFeedData();
      ReverseStop.reverseStop(sub, stop);
      var arrivals = [];
      var delays = [];
      // The below IIFE is what's calling the 'entity' property that is supposed
      // to be part of feed and which the TypeError is saying is undefined.
      (function dataFilter () {
        var feedObjs = GetFeedData.feed().entity.filter(function (entityObj) {
            if (entityObj.trip_update !== null && entityObj.trip_update.stop_time_update.stop_id == ReverseStop.stopIdN) {
                return entityObj.trip_update.stop_time_update;
            }
        });
      // Now a whole bunch more mutations happen to the array I should have in feedObjs. 
    };
})();

export const IsDelayS = (function () {
    // This is for all intents and purpses identical to IsDelayN
})();

1 个答案:

答案 0 :(得分:1)

网上有很多关于回调和承诺的资料。我认为在继续阅读更多内容之前,你会获得最大的里程数,因为它是Web开发的必要组成部分(处理异步内容)。跳过你最后的评论:

var feedObjs = GetFeedData.getFeedData().entity.filter(etc)

getFeedData()应该返回一个承诺。

getFeedData: function () {
  return rp({ // <--- make sure your function returns
    method: 'GET',
    url: 'https://cors-anywhere.herokuapp.com/http://datamine.mta.info/mta_esi.php?key=MY_KEY&feed_id=' + feedId,
    encoding: null
  }).then((buf) => {
      return GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(buf);
  });
}

任何返回承诺的内容都可以通过链接.then

来解决
export const IsDelayN = (function () {
  let noDelay, yesDelay;
  return {
    isDelay: function (sub, stop) {
      GetFeedData.getFeedId(sub);
      // ...
      GetFeedData.getFeedData().then(function (entity) {
        // code that needs to happen after async call goes here:
        (function dataFilter () {
          entity.filter(function (entityObj) {
              if (entityObj.trip_update !== null && entityObj.trip_update.stop_time_update.stop_id == ReverseStop.stopIdN) {
                  return entityObj.trip_update.stop_time_update;
              }
          });
        })()
      })
    }
  }
})();

基本上,一旦你需要的是异步,任何在它之后编写的代码必须写在异步调用返回之后的范围内(如回调或.then函数),而不是在&#34;下一行&#34;它将在异步调用返回之前执行。

这是一篇关于承诺的好文章:https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html