Клуб API Карт

API 2, geoXml.load, promise - неясности [SOLVED]

Пост в архиве.

Имеется:

var myLayers=[
{name='Группа A', xmlUrl:'...some_url.xml', active:0},
{name='Группа Б', xmlUrl:'...other_url.xml', active:0},
....
{name='Группа Z', xmlUrl:'...yet_another_url.xml', active:0}
];

 (Массив заранее не задан, генерируется отдельно и инклюдится апачем)

 В API 1.0 работала конструкция:

for (var i=0; i < myLayers.length; i++) {
    myLayers[i].obj=new YMaps.YMapsML(myLayers[i].xmlUrl,{ viewAutoApply: false });
     // далее создаем (вне карты) кнопку, включающую-выключающую i-ый слой через
     // map.addOverlay(myLayers[i].obj) и map.removeOverlay(myLayers[i].obj)
}

 
 В API 2.0 сходная конструкция - 

 

for (var i=0; i < myLayers.length; i++) {
     ymaps.geoXml.load(myLayers[i].xmlUrl).then(function(res) {myLayers[i].obj=res;});
     // и т.д.
}

- не работает, поскольку geoXML.Load асинхронен. К моменту выполнения then, переменная i успевает принять значение myLayers.length

 

 Что делать?

Можно, конечно, генерить вместе с массивом еще и "наполнитель" вида

 

ymaps.geoXml.load(myLayers[0].xmlUrl).then(function(res) {myLayers[0].obj=res;});
ymaps.geoXml.load(myLayers[1].xmlUrl).then(function(res) {myLayers[1].obj=res;});
...

 Но это явно не самый удобный путь.

 

Можно ли передавать в обработчик then дополнительный параметр, определенный на момент создания обработчика?

Или, может быть, можно заставить geoXml.load работать синхронно?

 

4 комментария

 

- не работает, поскольку geoXML.Load асинхронен. К моменту выполнения then, переменная i успевает принять значение myLayers.length

 Что делать?

Обычно код, использующий индекс оборачивают в self-invoking function

 

В первом АПИ все тоже было асинхронно, просто скрыто за интерфейсом оверлеев.

С promises все стало гораздо проще, да и интерфейс promises стандартизирован в CommonJS, а значит все реализации работают одинаково.

 

Или, может быть, можно заставить geoXml.load работать синхронно?

Это на самом деле не нужно и даже вредно, javascript это же не php (слава богу)

и все что асинхронно - для него естественно.

Схожую задачу я решал когда писал класс множественного геокодирования,

в данном случае подход будет тот же самый

Спасибо, работает!

 

Привожу код (если кому понадобится)

 

//  Обертка

function YmlForArrElem(index) {
     this._index = index || 0;
}
YmlForArrElem.prototype.load=function(XmlUrl){
       var self=this;
       ymaps.geoXml.load(XmlUrl).then( function(res){
                myLayers[self._index].obj=res.geoObjects;

                 // далее создаем кнопку-выключатель слоя

                // и т.п.
         });
  return self._index;

}

// Вызов:

...

for (var i=0; i < myLayers.length; i++) {

    var reader= new  YmlForArrElem(i);
    reader.load(myLayers[i].xmlUrl);
}

...

 

я бы передавал в метод load весь массив урлов, и в нем бы итерировался

и на выходе возвращал бы один GeoObjectCollection с вложенными группами загруженных YMapsML-ей

А при этом не получилась бы та же ситуация, что в исходном (неработающем) варианте?