处理 AJAX 调用中超出作用域的变量

3
我有以下代码:
var User = {
  get: function (options) {
    var self = this;

    $.ajax({
      url: options.url,
      success: function (data, response) {
        self.nextPageUrl = data.pagination.next_page;
        options.success(data, response);
      }
    });
  },
  nextPage: function (success) {
    this.get({
      url: this.nextPageUrl, 
      success: success
    });
  }
}

User.get({
  url: 'https://cache.getchute.com/v2/albums/aus6kwrg/assets',
  success: function (data, response) {
    // Through `data.pagination.next_page` I can get the URL
    // of the next page.
  }
});

User.nextPage({
  success: function (data, response) {
    // Here I want to make the same request but using the next_page
    // based on the next related to the previous' one.
  }
});

问题

基本上,我想根据前置请求(User.get())执行nextPage()操作,但由于其异步性,nextPage()方法不知道this.nextPageUrl属性——它会返回预期的undefined

最后,问题是:有人能想出一种方法来保持当前语法流程,但解决这个问题吗?实际上,有办法吗?

不,我不能使用同步请求。

常识

我想使用事件机制来处理这个问题:当请求被发出并调用.nextPage()时,尝试监听一个事件,让它在x秒内被触发,然后我希望this.nextPageUrl属性在该基于事件的范围内可用。

你们觉得呢?

免责声明:next_page的逻辑是由服务器预处理的,然后才发送到客户端。我没有使用增量/减量行为操作的选项。


如果您想尝试解决这个问题,请点击这里进入 jsFiddle

get 返回一个 Promise 并使用结果调用 nextPage。实际上,如果你只是返回那个 $.ajax 请求,那就是一个 Promise。 - Evan Davis
为什么是“-1”?我真的很想理解——这个问题的质量不好吗? - Guilherme Oderdenge
我认为问题出在this.nextPageUrl上。 this指向的不是User。如果你使用User.nextPageUrl,就可以在回调范围之外访问它。 - Lim H.
+1 @GuilhermeOderdenge - 我认为你提出了一个范围明确、易于重现的问题,并且还提供了一个易于重现的代码示例。我希望更多的问题都能像这样。请不要删除它,也不要因为一次无端的负评而灰心丧气。虽然这可能是一个经常遇到的问题,但你已经很好地表达了它。 - Travis J
@LimH。这不是作用域的问题。是我疏忽了,把它留下来了。 - Guilherme Oderdenge
显示剩余2条评论
3个回答

1
在发起异步请求之前,您可以获取对用户对象的引用。
var User = {
    get: function (options) {
    var self = this;
    $.ajax({
        url: options.url,
        success: function (data, response) {
          self.nextPageUrl = data.pagination.next_page;
          options.success(data, response);
        }
    });
},

我不会使用 self。因为 window 是隐式的,你会覆盖它隶属于 window 对象的可能性,所以你必须输入 window.self 而不是简单地输入 self - StackSlave

1

jsFiddle演示

有几种选择。您可以将属性的设置器绑定到同时调用 nextPage 的函数,您可以每 n 毫秒轮询调用 nextPage 直到 nextPageUrl 属性被填充,您可以使用 Promise,您可以使用 nextPageQueue。

我认为队列可能是完成这个任务最简单的形式。我还认为在此情况下让用户存储一些本地变量可能会很有用,并且使用函数对象可能更符合要求。

它会像这样:

var User = new function(){
  var pageQueue = [];
  var get = this.get = function (options) {
    $.ajax({
      url: options.url,
      dataType: 'JSON',
      success: function (data, response) {
        options.success(data, response);
        var nextPageUrl = data.pagination.next_page;
        if( pageQueue.length > 0 ){
         pageQueue[0](nextPageUrl);
         pageQueue.splice(0,1);
        }
      }
    });
  };
  var nextPage = this.nextPage = function (options) {
    pageQueue.push(function(nextPageUrl){
      get({
        url: nextPageUrl,
        success: options.success
      });
    });
  };
};

而且您的电话不会改变。

User.get({
  url: 'https://cache.getchute.com/v2/albums/aus6kwrg/assets',
  success: function (data, response) {
    // Through `data.pagination.next_page` I can get the URL
    // of the next page.
      console.log('get');
      console.log(data);
      console.log(response);
  }
});

User.nextPage({
  success: function (data, response) {
    // Here I want to make the same request but using the next_page
    // based on the next related to the previous' one.
    console.log('next');
    console.log(data);
    console.log(response);
  }
});

太棒了,它像魔法一样奏效!非常感谢!但我不太明白你具体做了什么——能再解释一下吗?比如,pageQueue.push(function (nextPageUrl(){}); 如何知道 nextPageUrl 是什么?我无法理解这是怎么发生的。 - Guilherme Oderdenge
@GuilhermeOderdenge - 这将一个函数推入一个只有一个参数nextPageUrl的数组中。这意味着它期望被调用时传入一个变量作为nextPageUrl的值。在get函数内部,如果队列不为空,则调用队列中的第一个函数,并提供data.pagination.next_page的值作为第一个推送的函数的nextPageUrl值。 - Travis J

0

您可以修改您的get方法并完全消除您的nextPage方法:

var User = {
  url: 'https://cache.getchute.com/v2/albums/aus6kwrg/assets',
  get: function (options) {
    var self = this;

    self.pageXhr = $.ajax({
      url: self.nextPageUrl ? self.nextPageUrl : self.url,
      dataType: 'JSON',
      success: function (data, response) {
        self.nextPageUrl = data.pagination.next_page;
        self.pageXhr = false;
        options.success(data, response);
      }
    });
  }
}

每当您调用User.get时,它将调用当前页面或下一个页面。不确定您获取后续页面的上下文,但如果您需要请求排队,请等待现有请求完成后再触发下一个请求。例如:
if (self.pageXhr) {
   self.pageXhr = self.pageXhr.then(function() {
      return $.ajax({
          url: self.nextPageUrl ? self.nextPageUrl : self.url,
          dataType: 'JSON',
          success: function (data, response) {
            self.nextPageUrl = data.pagination.next_page;
            options.success(data, response);
          }
      });
   });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接