2017-02-23 128 views
3

Мы разрабатываем API Excel JavaScript уже несколько месяцев. Мы сталкиваемся с проблемами, связанными с контекстом, которые были решены по неизвестным причинам. Мы не смогли воспроизвести эти проблемы и задались вопросом, как они решены. В последнее время подобные проблемы начали появляться снова. Ошибка, которую мы постоянно получаем:range.address вызывает ошибки, связанные с построением

Недвижимость «адрес» недоступен. Перед чтением значения свойства свойства вызовите метод загрузки для объекта-объекта и вызовите «context.sync()» в контексте связанного запроса.

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

var ContextManager = (function() { 
     var xlContext;//single context for entire project/application. 
     function loadContext() { 
      xlContext = new Excel.RequestContext(); 
     } 
     function sync(object) { 
      return (object === undefined) ? xlContext.sync() : xlContext.sync(object); 
     } 
     function getWorksheetByName(name) { 
      return xlContext.workbook.worksheets.getItem(name.toString()); 
     } 
     //public 
     return { 
      loadContext: loadContext, 
      sync: sync, 
      getWorksheetByName: getWorksheetByName 
     }; 
    })(); 

ПРИМЕЧАНИЕ: код выше укорочен. Существуют и другие методы для обеспечения использования единого контекста во всех приложениях. При реализации единого контекста, на этот раз, мы смогли повторить проблему.

Office.initialize = function (reason) { 
     $(document).ready(function() { 
      ContextManager.loadContext(); 
      function loadRangeAddress(rng, index) { 
       rng.load("address"); 
       ContextManager.sync().then(function() { 
        console.log("Address: " + rng.address); 
       }).catch(function (e) { 
        console.log("Failed address for index: " + index); 
       }); 
      } 
      for (var i = 1; i <= 1000; i++) { 
       var sheet = ContextManager.getWorksheetByName("Sheet1"); 
       loadRangeAddress(sheet.getRange("A" + i), i);//I expect to see a1 to a1000 addresses in console. Order doesn't matter. 
      } 
     }); 
    } 

В вышеуказанном случае только «A1» печатается как адрес диапазона на консоль. Я не вижу ни одного из других адресов (от A2 до A1000). Выполняется только блок catch. Может ли кто-нибудь объяснить, почему это происходит? Хотя я написал для цикла выше, это не мой прецедент. В реальном использовании такие ситуации возникают, когда одному объекту диапазона в функции a требуется загрузить адрес диапазона. В то время как другая функция b также хочет загрузить адрес диапазона. Обе функции a и функция b работают асинхронно на отдельных задачах, таких как создание объекта таблицы (адрес таблицы) и другие данные пасты на листе (есть инструкция отладки, чтобы увидеть, где были вставлены данные).

Это то, что наша команда не смогла найти или найти решение.

ответ

2

В этот код много, но проблема заключается в том, что вы вызываете синхронизацию целую кучу раз, не ожидая предыдущей синхронизации.

Есть несколько проблем с этим:

  • Если вы использовали различных контекстов, вы на самом деле видите, что существует предел ~ 50 одновременных запросов, после чего вы получите ошибки.
  • В вашем случае вы сталкиваетесь с другой (и почти противоположной) проблемой. Учитывая асинхронный характер API-интерфейсов и тот факт, что вы не ожидаете на sync-s, ваш первый запрос sync (который, как вам кажется, будет только для A1), фактически будет содержать все запросы load от выполнения целая for петля. Теперь, когда этот первый sync отправлен, очередь действий будет очищена. Это означает, что ваш второй, третий и т. Д. sync увидит, что нет ожидающей работы, и будет no-op, выполнив до того, как первая синхронизация когда-либо вернется со значениями!
    • [Это может считаться ошибкой, и я поговорю с командой о ее исправлении.Но это все еще очень опасная вещь, чтобы не ждать завершение sync перед тем, как перейти к следующей партии инструкций, которые используют один и тот же контекст.]

Исправление дождаться синхронизации. Это далеко не так просто сделать в TypeScript 2.1 и его функции async/await, в противном случае вам нужно сделать асинхронную версию цикла for, которую вы можете найти, но она довольно неинтуитивная (требуется создание uber-обещания, которое сохраняет цепочку связка .then -s)

Таким образом, ваш модифицированный машинопись маньяков код будет

ContextManager.loadContext(); 
async function loadRangeAddress(rng, index) { 
    rng.load("address"); 
    await ContextManager.sync().then(function() { 
     console.log("Address: " + rng.address); 
    }).catch(function (e) { 
     OfficeHelpers.Utilities.log(e); 
    }); 
} 
for (var i = 1; i <= 1000; i++) { 
    var sheet = ContextManager.getWorksheetByName("Sheet1"); 
    await loadRangeAddress(sheet.getRange("A" + i), i);//I expect to see a1 to a1000 addresses in console. Order doesn't matter. 
} 

Обратите внимание на асинхронной перед функцией loadRangeAddress, а два ждут -s перед от ContextManager.sync() и loadRangeAddress.

Обратите внимание, что этот код также будет работать довольно медленно, так как вы делаете асинхронную обратную линию для каждой ячейки. Это означает, что вы используете , не используя пакетную обработку, которая находится в самом ядре объектной модели для новых API.

Для полноты я должен также отметить, что создание «необработанного» RequestContext вместо использования Excel.run имеет некоторые недостатки. Excel.run делает ряд полезных вещей, наиболее важным из которых является автоматическое отслеживание объектов и их отключение (здесь не уместно, поскольку вы только читаете данные, но были бы важны, если бы вы загружались, а затем хотели бы написать обратно объект).

Наконец, если я могу порекомендовать (полное раскрытие: я автор книги), вы, вероятно, найдете полезную информацию о Office.js в электронной книге «Надстройки здания Office с использованием Office .js ", доступный по адресу https://leanpub.com/buildingofficeaddins. В частности, он имеет очень подробный (10-страничный) раздел о внутренней работе объектной модели («Раздел 5.5: Детали реализации, для тех, кто хочет знать, как это работает»). Он также предлагает советы по использованию TypeScript, имеет общий праймер Promise/async-await, описывает, что делает .run, и имеет более подробную информацию об OM. Кроме того, хотя он еще не доступен, он вскоре предоставит информацию о том, как резюме, используя тот же контекст (используя более новую технику, чем то, что было изначально описано в How can a range be used across different Word.run contexts?). Книга - скудная публикация «вечнозеленых» книг, сынок, как только я напишу эту тему в ближайшие недели, обновление будет доступно всем существующим читателям.

Надеюсь, это поможет!