Я разрабатываю надстройку панели задач Excel, которая записывает и считывает данные из диапазонов по сообщению WebSockets (используя socket.io). Эти операции занимают 4-5 секунд, если я не присоединяю отладчик (будь то DevTools или Visual Studio), который заметно повышает производительность (примерно до 100 мс). Отладчику даже не нужно оставаться подключенным, улучшение производительности сохраняется до перезапуска надстройки.
Некоторые детали:
- Диапазоны обычно не больше 10 ячеек.
- Я пытался использовать как отладочную, так и неотладочную версии Office.js
- Проблема возникает при операциях чтения и записи
- Я на 99% уверен, что задержка происходит в функциях Excel.run (и не имеет ничего общего с socket.io или другой библиотекой)
- Моя версия Excel - 1902 (сборка 11328.20146).
- Я использую TypeScript и Webpack для связывания надстройки.
- Я пробовал полифилл Promise для Bluebird и ES6-Promise
Единственная ссылка, которую я нашел до сих пор об этой проблеме, это этот вопрос , на который так и не получил ответа. Кажется, он испытал ту же проблему, что и я.
Пример операции чтения:
public async ReadObjectAsync(row)
{
var thisReader = this;
return new Promise(async (resolve, reject) =>
{
try
{
await Excel.run(async (context) =>
{
var object = {}
var boundingRectangle : Excel.Range = null;
var mappedControllers = QuoteAttributeArray.map(function(attribute) // Creamos un objeto con pares de atributo y controlador
{
return arrayFind(thisReader.AppContext.AttributeControllers, function(controller)
{
return controller.TargetAttribute == attribute;
})
});
for(var i = 0; i < QuoteAttributeArray.length; i++)
{
var attribute = QuoteAttributeArray[i];
var controller = (mappedControllers[i] as ExcelAttributeController);
if(controller.ColumnSelector.SelectedColumnLetter == null)
{
object[attribute] = null;
continue;
}
var ws = context.workbook.worksheets.getActiveWorksheet();
var targetRange = ws.getRange(controller.ColumnSelector.SelectedColumnLetter + row);
if(boundingRectangle == null)
{
boundingRectangle = targetRange;
}
else
{
boundingRectangle = boundingRectangle.getBoundingRect(targetRange); // Construimos el bounding rectangle atributo por atributo
}
if(controller.AttributeInput instanceof PlazoAttributeInput)
{
targetRange.load('text');
}
else
{
targetRange.load('values');
}
boundingRectangle.load('address');
await context.sync();
if(controller.AttributeInput instanceof PlazoAttributeInput)
{
object[attribute] = targetRange.text[0];
}
else
{
object[attribute] = targetRange.values[0];
}
}
var result = {
'boundingRectangleAddress' : boundingRectangle.address,
'object' : object
};
resolve(result);
});
}
catch(error)
{
reject(error);
}
})
}
Для некоторого контекста: эта функция возвращает объект, представляющий книгу, как в
{
isbn: '9788798',
title : 'Blahblah'
author : 'Blahblah'
}
, который читается из одной строки в Excel. Элементы в "QuoteAttributeArray" - это различные атрибуты книги (максимум 15), которые ранее были "сопоставлены" со столбцами.
Эта же функция выполняется 4 секунды при запросе из сообщения сокета, 1 секунда при выполнении запроса внутри надстройки и 100 мс (из сокета и внутри надстройки) после подключения отладчика.
Решения пробовали
Я реорганизовал функцию, чтобы сделать только один вызов context.sync (). Он значительно улучшается (до ~ 300 мс при первом выполнении и ~ 30 мс после, без отладчика), но есть разница, когда я присоединяю отладчик (~ 20 мс или меньше с первого раза). Я предполагаю, что это означает, что множественные вызовы context.sync () только увеличивают задержку, но она все еще там. Вот моя переработанная версия:
public async ReadObjectAsync(row)
{
var thisReader = this;
return new Promise(async (resolve, reject) =>
{
try
{
return await Excel.run(async (context) =>
{
var object = {}
var boundingRectangle : Excel.Range = null;
var mappedControllers = QuoteAttributeArray.map(function(attribute) // Creamos un objeto con pares de atributo y controlador
{
return arrayFind(thisReader.AppContext.AttributeControllers, function(controller)
{
return controller.TargetAttribute == attribute;
})
});
let attributeRanges = {};
for(let i = 0; i< QuoteAttributeArray.length; i++)
{
var attribute = QuoteAttributeArray[i];
var controller = (mappedControllers[i] as ExcelAttributeController);
if(controller.ColumnSelector.SelectedColumnLetter == null)
{
object[attribute] = null;
continue;
}
var ws = context.workbook.worksheets.getActiveWorksheet();
var targetRange = ws.getRange(controller.ColumnSelector.SelectedColumnLetter + row);
if(boundingRectangle == null)
{
boundingRectangle = targetRange;
}
else
{
boundingRectangle = boundingRectangle.getBoundingRect(targetRange); // Construimos el bounding rectangle atributo por atributo
}
if(controller.AttributeInput instanceof PlazoAttributeInput)
{
console.log('attribute :' + attribute)
targetRange.load('text');
}
else
{
console.log('nonPlazo :' + attribute)
targetRange.load('values');
}
attributeRanges[attribute] = targetRange;
}
boundingRectangle.load('address');
return context.sync().then(() =>
{
for(var attribute in attributeRanges)
{
if(attribute == 'plazo')
{
object[attribute] = attributeRanges[attribute].text[0];
}
else
{
object[attribute] = attributeRanges[attribute].values[0];
}
}
var result = {
'boundingRectangleAddress' : boundingRectangle.address,
'object' : object
};
return resolve(result);
});
});
}
catch(error)
{
return reject(error);
}
})
}