LINUX.ORG.RU

JavaScript: в callback функции теряется контекст. Как это обойти?


0

1

Разбираюсь значит с загрузкой данных с сервера и событийной моделью в JavaScript.

У меня есть «класс», в нем нужно сделать инит, который включет загрузку данных с сервера и их небольшую обработку. Для инита и загрузки данных используется цепочка функций. Вторая в цепочке инита функция вызывается как callback после получения данных с сервера. В общем, всё как учили.

Код примерно такой:

function LBTablesWorker() 
{

 // Начало инициализации
 this.init = function(iTeamId) 
 {
  // Загрузка с сервера
  this.load(iTeamId, this.initNext); 
 }; 
 

 // Продолжение инициализации
 this.initNext = function() 
 {
  alert('In LBTablesWorker::initNext()'); // Это сообщение видно
  
  this.calculate(); // Здесь ошибка
 }

 
 // Вот эта функция не вызывается
 this.calculate=function() 
 {
  alert('2*2=4');
 };

 
 // Загрузка данных с сервера
 this.load=function(teamId, callback) 
 {
  var that = this;

  // Получение XML-кода  
  $.xmlrpc({
        url: 'http://'+window.location.host+'/XmlRpcServer',
        methodName: 'getTables',
        params: [teamId],
        success: function(response, status, jqXHR){ that.successLoad(response, status, jqXHR); callback(); },
        error: function(jqXHR, status, error) { that.errorLoad(jqXHR, status, error); }
  });

 };
}

Проблема в том, что по всей видимости, при вызове callback-функции теряется контекст. И в такой функции невозможно вызвать функции собственного же класса.

Ошибка в Opera:

Uncaught exception: TypeError: 'this.calculate' is not a function

Ошибка в FireFox:

TypeError: this.calculate is not a function initNext

Вопрос. Как это можно обойти? Как правильно вызывать callback-функции, чтобы они выполнялись в контексте того места, которому изначально принадлежат?

Note: Да, я не хочу использовать лямбды, я хочу чтобы мой код был как минимум читабельный.

★★★★★

Note: Да, я не хочу использовать лямбды,
this.init = function(iTeamId)
this.initNext = function()

....

/0

ты привел не весь код из которого не понятно что ты делаешь.
сделай alert(this) и alert(this.calculate) и посмотри что это.

r ★★★★★
()
Ответ на: комментарий от r

В функции this.init(): This это [object Object]

В функции this.initNext(): This это [object Windows]

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от r

Всё там понятно, кода даже слишком много.

Если класс нужен только чтобы объединить методы над одной сущностью и сущность притом создаваться будет только один раз, то можно просто имя классаобъекта везде использовать вместо this.

Иначе call/apply

Kalashnikov ★★★
()
Ответ на: комментарий от Kalashnikov

Ну с call() понятно, в данном случае оно поможет.

Но есть у меня еще один случай.

Предположим, инит объекта вызывается из объекта более «верхнего» уровня. Вот так:

function UpLevelClass() 
{

 this.init = function() 
 {
  alert('Up level class this 1:'+this);

  worker=new Worker();
  worker.init(this.initNext);
 }; 

 this.initNext = function() 
 {
  alert('Up level class this 2:'+this);
 };

}
function Worker() 
{

 this.init = function(upLevelCallBack) 
 {
  ... всякие действия для инита

  upLevelCallBack();
 };

Спрашивается, в каком контексте будет выполнена функция initNext()? По-моему, совсем не в контесте класса UpLevelClass.

Теперь вопрос. Как в функции init() класса Worker определить контекст функции upLevelCallBack? Ведь нам ее надо вызвать в контексте класса UpLevelClass, а не в текущем контексте.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

не знаю где у тебя горит но УМВР. Покажи как зовется init.

r ★★★★★
()
// Продолжение инициализации
 this.initNext = function() 
 {
  alert('In LBTablesWorker::initNext()'); // Это сообщение видно
  
  this.calculate(); // Здесь ошибка
 }

точка с запятой пропущена - это такой замысел?

swwwfactory ★★
()
Ответ на: комментарий от vertexua

Используй self.

Ты не понял.

Нужно узнать This верхнего уровня, чтобы корректно вызвать upLevelCallBack.

Как это сделать?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Там такой же self.initNext нужно передавать, перед этим сделав var self=this наверху. То что написано в твоем примере вызовет this.init.initNext, которого нет. this - это текущая функция, а не какой-то там контекст выше

Рекомендую запомнить var self=this как мантру. Еще лучше запомни как мантру «никогда не использовать this, кроме как в var self=this». И да, если selfы разных уровней совпадают, придумай им другие имена

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 4)
Ответ на: комментарий от vertexua

Кароче, вы ничего не поняли, но я обошел проблему.

Для того чтобы по-человечески вызывать коллбек-функцию, недостаточно запомнить ссылку на нее, нужно еще запомнить контекст.

Поэтому в надо передавать не просто функцию и потом ее вызывать:

worker.init(this.initNext);

function Worker() 
{

 this.init = function(upLevelCallBack) 
 {
  ... всякие действия для инита

  upLevelCallBack();
 };

а нужно передавать функцию вместе с контекстом и потом ее вызывать в данном контексте:

worker.init( [this, this.initNext] );

function Worker() 
{

 this.init = function(upLevelCallBack) 
 {
  ... всякие действия для инита

  upLevelCallBack[1].call( upLevelCallBack[0] );
 };
Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

OMG. Давай так, то что я сказал, не помогло?

function UpLevelClass() 
{
 var self=this;
 self.init = function() 
 {
  alert('Up level class this 1:'+this);

  worker=new Worker();
  worker.init(self.initNext);
 }; 

 self.initNext = function() 
 {
  alert('Up level class this 2:'+this);
 };

}
function Worker() 
{
 var self=this;
 self.init = function(upLevelCallBack) 
 {
  ... всякие действия для инита

  upLevelCallBack();
 };
vertexua ★★★★★
()
Ответ на: комментарий от vertexua

Напишите хоть

worker.init(function(){self.initNext();});
раз на то пошло

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 2)

var that = this

тред не читал, даже оппост, извините

trashymichael ★★★
()
Ответ на: комментарий от vertexua

OMG. Давай так, то что я сказал, не помогло?

Ты сказал какой-то обрывок мысли. Расшифровывать его у меня нет никакого желания. Код ты наконец-то привел тогда, когда проблема была решена. Так что учись внятно объяснять, или будешь бесполезен для общества.

Возможно я этим методом воспользуюсь в будущем, но сейчас переделывать то что сделал через [context, function], уже, конечно, не буду.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от vertexua

Да, и ваша конструкция

function UpLevelClass() 
{
 var self=this;
 self.init = function()


будет нормально работать с несколькими объектами одного класса, или пригодна только для синглтона?

Xintrea ★★★★★
() автор топика
Последнее исправление: Xintrea (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.