Chastniimastertver.ru

Ремонт бытовой техники
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Хаббл выздоравливает: когда NASA вернет космический телескоп к работе

Хаббл выздоравливает: когда NASA вернет космический телескоп к работе

телескоп, Хаббл, Hubble, космос

Команда телескопа Хаббл продолжает устранять неполадки в работе приборов и «полное выздоровление», скорее всего, не за горами.

По словам представителей NASA, инженеры достигли определенного прогресса в процессе восстановления работоспособности приборов космического телескопа Хаббл. Из пяти научных приборов телескопа один уже работает, а теперь команда телескопа попытается вернуть к работе камеру Wide Field Camera 3. По прогнозам специалистов, для полного возобновления научной деятельности с помощью всех приборов телескопа, специалистам понадобиться несколько недель, сообщает Space.

ФОКУС в Google Новостях.

Подпишись — и всегда будь в курсе событий.

В конце октября у космического телескопа Хаббл возникли проблемы с синхронизацией внутренних сообщений, в результате чего отключились все 5 его научных приборов. Специалистам удалось возобновить работу одного из них – камеры Advanced Camera for Surveys, но остальные 4 прибора остаются в защитном безопасном режиме.

У телескопа Хаббл 5 научных приборов:

  • камера Advanced Camera for Surveys, которая снова работает;
  • камера Wide Field Camera 3;
  • камера Near Infrared Camera и спектрометр Multi-Object Spectrometer;
  • спектрограф Cosmic Origins;
  • спектрографом формирования изображений Space Telescope Imaging Spectrograph;

телескоп, Хаббл, Hubble, космос

Первые результаты

Команда телескопа Хаббл прикладывает все усилия для скорейшего выхода из безопасного режима всех остальных четырех приборов, и они добились определенных результатов.

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

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

В ближайшее время инженеры проверят эффективность потенциальных изменений и составят план возобновления работы для 4 оставшихся в безопасном режиме приборов телескопа Хаббл.

В NASA говорят, что восстанавливать приборы будут по очереди и на это потребуется точно несколько недель.

Есть один прибор, который не требует особых изменений в работе и, который можно восстановить быстрее, говорят в NASA. Это камера Wide Field Camera 3, которую хотят вернуть к работе в ближайшие дни, как это уже было сделано с камерой Advanced Camera for Surveys.

Как синхронизировать основные данные iPhone с веб-сервером, а затем отправить их на другие устройства?

Я работал над способом синхронизации основных данных, хранящихся в приложении iPhone, между несколькими устройствами, такими как iPad или Mac. Существует не так много (если таковые имеются) синхронизирующих сред для использования с Core Data на iOS. Тем не менее, я думал о следующей концепции:

  1. Внесено изменение в локальное базовое хранилище данных, и оно сохранено. (a) Если устройство подключено к сети, оно пытается отправить набор изменений на сервер, включая идентификатор устройства, отправившего набор изменений. (b) Если набор изменений не достигает сервера, или если устройство не подключено к сети, приложение добавит набор изменений в очередь для отправки, когда оно появится в сети.
  2. Сервер, находящийся в облаке, объединяет определенные наборы изменений, которые он получает, со своей основной базой данных.
  3. После объединения набора изменений (или очереди наборов изменений) на облачном сервере сервер передает все эти наборы изменений на другие устройства, зарегистрированные на сервере, с использованием некоторой системы опроса. (Я думал использовать сервисы Apple Push, но, судя по комментариям, это не работоспособная система.)

Есть ли что-нибудь необычное, о чем мне нужно подумать? Я посмотрел на REST фреймворки, такие как ObjectiveResource , Базовый ресурс и RestfulCoreData . Конечно, все они работают с Ruby на Rails, с которыми я не связан, но это место для начала. Основные требования, которые я предъявляю к своему решению:

  1. Любые изменения следует отправлять в фоновом режиме, не останавливая основной поток.
  2. Следует использовать как можно меньшую пропускную способность.

Я подумал о ряде проблем:

  1. Убедитесь, что идентификаторы объектов для разных хранилищ данных на разных устройствах прикреплены к серверу. То есть у меня будет таблица идентификаторов объектов и идентификаторов устройств, которые связаны через ссылку на объект, хранящийся в базе данных. У меня будет запись (DatabaseId [уникален для этой таблицы], ObjectId [уникален для элемента всей базы данных], Datafield1, Datafield2), поле ObjectId будет ссылаться на другую таблицу, AllObjects: (ObjectId, DeviceId, DeviceObjectId). Затем, когда устройство выдвигает набор изменений, оно передает идентификатор устройства и идентификатор объекта из основного объекта данных в локальном хранилище данных. Затем мой облачный сервер проверит соответствие objectId и идентификатора устройства в таблице AllObjects и найдет запись для изменения в исходной таблице.
  2. Все изменения должны иметь временные метки, чтобы их можно было объединить.
  3. Устройству придется опрашивать сервер, не расходуя слишком много батареи.
  4. Локальные устройства также должны будут обновлять все, что хранится в памяти, если/когда изменения получены от сервера.

Есть ли что-то еще, что мне здесь не хватает? На какие рамки мне следует обратить внимание, чтобы сделать это возможным?

Читайте так же:
Синхронизировать клавиатуру с андроидом

Я предлагаю внимательно прочитать и реализовать стратегию синхронизации, обсуждаемую Дэном Гровером на конференции iPhone 2009, доступную здесь в виде документа в формате PDF.

Это жизнеспособное решение, и его не так сложно реализовать (Дэн реализовал это в нескольких своих приложениях), перекрывая решение, описанное Крисом. Подробное теоретическое обсуждение синхронизации см. В статье Русса Кокса (MIT) и Уильяма Джозефсона (Принстон):

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

Кажется, что файл PDF Гровера больше не доступен (неработающая ссылка, март 2015). ОБНОВЛЕНИЕ: ссылка доступна через Way Back Machine здесь

Инфраструктура Objective-C, называемая ZSync и разработанная Маркусом Заррой, устарела, поскольку в конечном итоге iCloud, похоже, поддерживает правильную синхронизацию данных ядра.

Я сделал что-то похожее на то, что вы пытаетесь сделать. Позвольте мне рассказать вам, что я узнал и как я это сделал.

Я предполагаю, что между вашим объектом Core Data и моделью (или схемой БД) на сервере есть взаимно-однозначное отношение. Вы просто хотите синхронизировать содержимое сервера с клиентами, но клиенты также могут изменять и добавлять данные. Если я правильно понял, тогда продолжайте читать.

Я добавил четыре поля, чтобы помочь с синхронизацией:

  1. sync_status — добавьте это поле только в вашу базовую модель данных. Он используется приложением, чтобы определить наличие ожидающих изменений элемента. Я использую следующие коды: 0 означает отсутствие изменений, 1 означает, что он находится в очереди для синхронизации с сервером, а 2 означает, что это временный объект и его можно удалить.
  2. is_deleted — добавьте это в модель данных сервера и ядра. Событие Delete на самом деле не должно удалять строку из базы данных или из вашей модели клиента, потому что у вас не остается ничего для синхронизации. Имея этот простой логический флаг, вы можете установить is_deleted в 1, синхронизировать его, и все будут счастливы. Вы также должны изменить код на сервере и клиенте для запроса не удаленных элементов с «is_deleted = 0».
  3. last_modified — добавьте это в модель данных сервера и ядра. Это поле должно автоматически обновляться сервером с учетом текущей даты и времени всякий раз, когда что-либо изменяется в этой записи. Это никогда не должно быть изменено клиентом.
  4. guid — добавить глобально уникальный идентификатор (см. http://en.wikipedia.org/wiki/Globally_unique_identifier ) поле для сервера и базовая модель данных. Это поле становится первичным ключом и становится важным при создании новых записей на клиенте. Обычно ваш первичный ключ представляет собой инкрементное целое число на сервере, но мы должны помнить, что контент может быть создан в автономном режиме и синхронизирован позже. GUID позволяет нам создавать ключ в автономном режиме.

На клиенте добавьте код, чтобы задать для sync_status значение 1 для объекта модели всякий раз, когда что-то изменяется и его необходимо синхронизировать с сервером. Новые объекты модели должны генерировать GUID.

Синхронизация — это один запрос. Запрос содержит:

  • Максимальная отметка времени last_modified объектов вашей модели. Это говорит серверу, что вы хотите изменения только после этой отметки времени.
  • Массив JSON, содержащий все элементы с sync_status = 1.

Сервер получает запрос и делает это:

  • Он берет содержимое из массива JSON и изменяет или добавляет содержащиеся в нем записи. Поле last_modified автоматически обновляется.
  • Сервер возвращает массив JSON, содержащий все объекты с отметкой времени last_modified, превышающей отметку времени, отправленную в запросе. Это будет включать только что полученные объекты, что служит подтверждением того, что запись была успешно синхронизирована с сервером.

Приложение получает ответ и делает это:

  • Он берет содержимое из массива JSON и изменяет или добавляет содержащиеся в нем записи. Для каждой записи устанавливается значение sync_status, равное 0.

Надеюсь, это поможет. Я использовал запись и модель Word взаимозаменяемо, но я думаю, вы поняли идею. Удачи.

Если вы все еще ищете способ пойти, посмотрите на мобильный телефон Couchbase. Это в основном делает все, что вы хотите. ( http://www.couchbase.com/nosql-databases/couchbase-mobile )

Подобно @Cris, я реализовал класс для синхронизации между клиентом и сервером и до сих пор решил все известные проблемы (отправка/получение данных на сервер/с сервера, слияние конфликтов на основе временных меток, удаление дублированных записей в ненадежных сетевых условиях, синхронизация вложенных данных и файлы и т.д ..)

Вы просто сообщаете классу, какая сущность и какие столбцы должны синхронизироваться и где находится ваш сервер.

Вы можете найти источник, рабочий пример и другие инструкции здесь: github.com/knagode/M3Synchronization .

Я думаю, что хорошим решением проблемы GUID является «система распределенных идентификаторов». Я не уверен, каков правильный термин, но я думаю, что именно так назывались документы MS SQL Server (SQL использует/использовал этот метод для распределенных/синхронизированных баз данных). Это довольно просто:

Читайте так же:
Синхронизировать только смс на айфоне

Сервер назначает все идентификаторы. Каждый раз, когда выполняется синхронизация, первое, что проверяется: «Сколько идентификаторов у меня осталось на этом клиенте?» Если клиенту не хватает, он запрашивает у сервера новый блок идентификаторов. Затем клиент использует идентификаторы в этом диапазоне для новых записей. Это отлично подходит для большинства задач, если вы можете назначить блок достаточно большой, чтобы он «никогда» не заканчивался до следующей синхронизации, но не настолько большой, чтобы сервер со временем заканчивал работу. Если клиент когда-либо закончится, обработка может быть довольно простой, просто скажите пользователю «извините, что вы не можете добавить больше элементов, пока вы не синхронизируете» . если они добавляют столько элементов, не должны ли они синхронизироваться, чтобы избежать устаревших данных проблемы в любом случае?

Я думаю, что это лучше, чем использование случайных идентификаторов GUID, поскольку случайные идентификаторы GUID не являются безопасными на 100% и обычно должны быть намного длиннее стандартного идентификатора (128 бит по сравнению с 32 битами). У вас обычно есть индексы по идентификатору и вы часто храните их в памяти, поэтому важно, чтобы они были небольшими.

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

Я только что опубликовал первую версию моего нового API синхронизации Core Data Cloud, известного как SynCloud. SynCloud имеет много различий с iCloud, потому что он позволяет многопользовательский интерфейс синхронизации. Он также отличается от других синхронизирующих API-интерфейсов, поскольку позволяет использовать многотабличные реляционные данные.

Сборка с iOS 6 SDK, он очень актуален по состоянию на 27.09.2012.

Уведомление пользователя об обновлении данных с помощью Push-уведомления. Используйте фоновый поток в приложении, чтобы проверить локальные данные и данные на облачном сервере, в то время как изменение происходит на сервере, измените локальные данные, и наоборот.

Поэтому я думаю, что самая сложная часть — это оценка данных, в которых сторона недействительна.

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

Сначала вы должны переосмыслить, сколько у вас будет данных, таблиц и отношений. В моем решении я реализовал синхронизацию через файлы Dropbox. Я наблюдаю за изменениями в основном MOC и сохраняю эти данные в файлы (каждая строка сохраняется как gzipped json). Если работает интернет-соединение, я проверяю, есть ли какие-либо изменения в Dropbox (Dropbox дает мне дельта-изменения), загружаю их и объединяю (последние выигрыши), и, наконец, помещаю измененные файлы. Перед синхронизацией я помещаю файл блокировки в Dropbox, чтобы другие клиенты не синхронизировали неполные данные. При загрузке изменений безопасно, что загружаются только частичные данные (например, потеря соединения с интернетом). Когда загрузка завершена (полностью или частично), она начинает загружать файлы в Core Data. Когда возникают неразрешенные отношения (не все файлы загружаются), он прекращает загрузку файлов и пытается завершить загрузку позже. Отношения хранятся только как GUID, поэтому я могу легко проверить, какие файлы загружать, чтобы иметь полную целостность данных. Синхронизация начинается после внесения изменений в основные данные. Если изменений нет, то он проверяет наличие изменений в Dropbox каждые несколько минут и при запуске приложения. Дополнительно, когда изменения отправляются на сервер, я отправляю широковещательную рассылку другим устройствам, чтобы сообщить им об изменениях, чтобы они могли синхронизироваться быстрее. Каждый синхронизируемый объект имеет свойство GUID (guid также используется в качестве имени файла для файлов обмена). У меня также есть база данных Sync, где я храню ревизию Dropbox каждого файла (я могу сравнить ее, когда дельта-сброс сбрасывает состояние). Файлы также содержат имя объекта, состояние (удалено/не удалено), guid (так же, как имя файла), ревизию базы данных (для обнаружения миграции данных или во избежание синхронизации с никогда не версиями приложения) и, конечно, данные (если строка не удалена).

Это решение работает для тысяч файлов и около 30 объектов. Вместо Dropbox я мог бы использовать хранилище ключей/значений в качестве веб-службы REST, которую я хочу сделать позже, но у меня нет на это времени 🙂 На данный момент, на мой взгляд, мое решение более надежно, чем iCloud и , что очень важно, у меня есть полный контроль над тем, как это работает (в основном потому, что это мой собственный код).

Другое решение — сохранить изменения MOC как транзакции — с сервером будет обмениваться гораздо меньше файлов, но сложнее выполнить первоначальную загрузку в правильном порядке в пустые данные ядра. iCloud работает таким же образом, и другие решения для синхронизации имеют аналогичный подход, например TICoreDataSync .

Через некоторое время я перешел на Ансамбли — Я рекомендую это решение, а не изобретать велосипед.

Не удается синхронизировать почтовый ящик Exchange после установки устройства Apple iOS для iOS 7.0

Пользователь имеет почтовый ящик, который размещается на Microsoft Exchange Server 2010 или Microsoft Exchange Server 2013.

Читайте так же:
Сколько длится установка обновления системы андроид

Пользователь обновляет Apple iOS устройства iOS 7.0.

Пользователь пытается синхронизировать свои устройства iOS 7.0 с почтовым ящиком Exchange Server.

В этом случае пользователю не удается синхронизировать свои устройства iOS 7.0 с почтового ящика, размещенного на Exchange Server. Кроме того в журнале приложений на компьютере Exchange Server 2010 или на компьютере Exchange Server 2013 ролью клиентского доступа регистрируется следующее событие:

КОД: 4999
Уровень: ошибка
Источник: Общий MSExchange
Сообщение об ошибке: Watson отчет о готовности к передаче для идентификатора процесса: < идентификатор процесса, > с параметрами: E12, < тип >, < версия приложения > AirSync, M.Exchange.Compliance, M.E.S.C.MessageDigestForNonCryptographicPurposes.HashCore, об, < callstackHash >, < версии сборки >.

Кроме того в журнале Exchange ActiveSync регистрируется сообщение об ошибке, подобное приведенному ниже.

Примечание. Эта ошибка 110 состояния для подготовки команды, появляется много раз устройства iOS на основе 7.0 повторно отправляет команду подготовки после возвращения исключение сервера. Во время тестирования, некоторые устройства iOS команды подготовки более 2 000 раз, даже после отправки постоянного сбоя.

RequestHeader:
POST /Microsoft-Server-ActiveSync/default.eas? Пользователь = johndoe & DeviceId = ApplDMPGH8DWDFJ3 & DeviceType = iPad & Cmd = подготовки HTTP/1.1
Подключение: активности
Значение Content-Length: 136
Тип содержимого: application/vnd.ms-sync.wbxml
Принимать: * / *
Принять-Encoding: gzip, deflate
Принять язык: en-us
Авторизация: ***
Cookie: Файл Cookie = R3437444435
Узла: contosocas.com
User-Agent: Apple-iPad2C2/1101.465
MS-ASProtocolVersion: 14,1

RequestBody:
<?xml version=»1.0″ encoding=»utf-8″ ?>
< подготовить >
< DeviceInformation >
<Set>
<Model>iPad2C2</Model>
<UserAgent>iOS/7.0 (11A465) dataaccessd/1.0</UserAgent>
11A465 iOS 7.0 < OS > < /OS >
<OSLanguage>en</OSLanguage>
< FriendlyName > черный iPad 2 < / FriendlyName >
</Set>

Command_WorkerThread_Exception :
—— Начало исключения—
Тип исключения: об
Сообщение об исключении: требуется неотрицательное число.
Имя параметра: число
Уровень исключения: 0
Трассировка стека: в System.Buffer.BlockCopy (массив src, Int32 srcOffset, dst массив Int32 dstOffset, счетчик Int32)
в Microsoft.Exchange.Security.Compliance.MessageDigestForNonCryptographicPurposes.HashCore (массив Byte [], Int32 ibStart, Int32 cbSize)
в System.Security.Cryptography.HashAlgorithm.ComputeHash (буфер Byte [])
в Microsoft.Exchange.Configuration.SQM.SmsSqmDataPointHelper.Generate64BitUserID (строка legacyDN)
в Microsoft.Exchange.Configuration.SQM.SmsSqmDataPointHelper.AddDeviceInfoReceivedDataPoint (экземпляр SmsSqmSession, идентификатором ADObjectId, legacyDN строка, строка deviceType, строка versionString)

в Microsoft.Exchange.AirSync.DeviceInformationSetting.ProcessSet (XmlNode setNode)
в Microsoft.Exchange.AirSync.DeviceInformationSetting.Execute()
в Microsoft.Exchange.AirSync.ProvisionCommand.ProcessDeviceInformationSettings (XmlNode ответ)
в Microsoft.Exchange.AirSync.ProvisionCommand.ExecuteCommand()
в Microsoft.Exchange.AirSync.Command.WorkerThread()
—Конец исключения—

ResponseHeader:
HTTP/1.1 200 OK

ResponseBody:
<?xml version=»1.0″ encoding=»utf-8″ ?>
< подготовить >
<Status>110</Status>
</Provision>

Причина

Эта проблема возникает, поскольку несколько потоков ActiveSync и W3wp.exe пытаются получить доступ к защищенным объектом в то же время. Это переводит объект в некорректном состоянии.

Решение

Exchange Server 2013

Для решения этой проблемы в Exchange Server 2013, установить обновление, описанное в следующей статье базы знаний Майкрософт:

Описание накопительного обновления 2 для Exchange Server 2013

Exchange Server 2010

Для решения этой проблемы в Exchange Server 2010, установите накопительный пакет обновления, описанного в следующей статье базы знаний Майкрософт:

Описание накопительного пакета обновления 2 для Exchange Server 2010 с пакетом обновления 3

Статус

Проблема, в которой устройства iOS 7.0 отправляет многие команды резервов после возвращается ошибка сейчас исследуется компанией Apple. Эта статья будет обновлена, когда можно получить дополнительные сведения.

Поиск проблемных промисов в JavaScript

JavaScript — это фантастический язык для серверного программирования, так как он поддерживает асинхронное выполнение кода. Но это ещё и усложняет его использование.

Асинхронность означает возможность написания кода, который не подвержен обычным проблемам, связанным с потокобезопасностью, и поддерживает выполнение множества задач в «параллельном» режиме. Например — ожидание считывания файла не блокирует другие вычисления.

Но небольшие ошибки в асинхронном коде могут приводить к появлению неразрешённых промисов. То есть — к участкам кода, выполнение которых, однажды начавшись, никогда не завершается.

Мы столкнулись с этой проблемой, когда в пуле соединений нашей базы данных knex постоянно заканчивались доступные соединения, после чего происходил сбой сервиса. Обычно в рамках соединения выполняется запрос, после чего оно возвращается в пул и может быть использовано для выполнения другого запроса.

Эти соединения что-то захватывало.

Речь идёт о кодовой базе в миллионы строк кода, которой в течение нескольких лет занимались десятки программистов. Может — проблема заключается в неразрешённых промисах? Я решил исследовать этот вопрос.

Проблема останова поднимает голову

Поиск неразрешённых промисов — это пример попытки решения проблемы останова, а известно, что не существует общего алгоритма решения этой проблемы.

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

Google выдал мне несколько страниц, где обсуждают неразрешённые промисы. Эти обсуждения всегда сводятся к тому, что не надо писать изначально некачественный код.

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

Читайте так же:
Как отключить синхронизацию звонков между iphone и iphone

Именно поэтому и существуют тайм-ауты соединений. Если сервер остановился — мы не собираемся ждать его вечно. Это важно при разработке распределённых систем.

Но «убийство» процесса сервера при каждом недоразумении — это решение не идеальное. И, кроме того, оснащение каждого промиса в кодовой базе тайм-аутом — это, в лучшем случае, очень сложно.

Но не так всё плохо! Проблему останова можно решить для подмножества распространённых паттернов.

Паттерны проблемных промисов

В прочитанной мной публикации «Нахождение проблемных промисов в асинхронных JavaScript-программах» Алимадади с соавторами выделили распространённые паттерны, которые приводят к появлению неразрешённых промисов, и представили программу PromiseKeeper. Эта программа находит потенциально неразрешаемые промисы с использованием графа промисов.

▍Краткий рассказ о JavaScript-промисах

Та публикация начинается с обзора JavaScript-промисов.

Промисы представляют асинхронные вычисления и могут пребывать в трёх состояниях: pending (ожидание), fulfilled (успешное разрешение) и rejected (отклонение). Вначале они оказываются в состоянии pending .

Код, реагирующий на результат работы промиса (обработчик определённого события), регистрируют с помощью метода .then() .

В коде, на практике, опускают второй параметр для того чтобы сосредоточиться на коде, который выполняется при успешном разрешении промиса ( fulfilledReaction ). Обычно так поступают при построении цепочек промисов:

Каждый вызов .then() приводит к созданию нового промиса, который разрешается возвращаемым значением кода, реагирующего на завершение работы предыдущего промиса. Обратите внимание на то, что последний вызов неявно разрешается со значением undefined . Дело в том, что в JavaScript функция, которая не возвращает что-либо, неявным образом возвращает undefined .

В цепочку промисов можно добавить механизм обработки ошибок с использованием .catch() :

Каждый промис, созданный .then() , неявно определяет код, реагирующий на отклонение промиса, аналогичный конструкции err => throw err . Это значит, что .catch() в конце цепочки промисов может отреагировать на ошибки, возникшие в любом из предыдущих промисов.

На практике редко полагаются на стандартный механизм обработки успешно разрешённых промисов, но следующий код корректен:

Полагаю, подобное чаще происходит случайно, чем намеренно.

Промисы можно связывать, используя один промис для разрешения другого промиса:

Здесь состояние промиса p0 связано с p1 . То есть — неименованный промис, созданный в строке 3, отклоняется со значением foo .

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

▍Паттерн №1: необработанное отклонение промиса

Обычным источником ошибок при работе с промисами являются необработанные отклонения промисов.

Это происходит, когда неявно отклоняют промис, выдавая ошибку в коде, реагирующем на успешное завершение промиса:

Так как код, реагирующий на успешное завершение промиса, выполняется в отдельном асинхронном контексте, JavaScript не передаёт эту ошибку в главный поток. Ошибка «проглатывается» и разработчик никогда не узнает о том, что она произошла.

Исправить это можно, использовав метод .catch() :

Теперь у нас появляется возможность обработать ошибку.

Но мы не перевыбросили эту ошибку! Если другой промис, связанный с этим, или объединённый с ним в цепочку, полагается на этот код, ошибка будет оставаться «проглоченной». Код продолжит выполняться.

Попробуйте следующее в консоли браузера:

Можно ожидать, что в этом коде выполняются вычисления 17 + 1 = 18 , но, из-за неожиданной ошибки, мы получаем NaN . Промис, неявно созданный .catch() , неявно разрешается (а не отклоняется) со значением undefined .

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

▍Паттерн №2: незавершённые промисы

Новые промисы пребывают в состоянии pending до тех пор, пока не будут успешно разрешены или отклонены (то есть — завершены). Но если промис не завершается, его можно назвать остановившимся промисом. Он навсегда останется в состоянии pending , не давая выполняться коду, полагающемуся на его завершение.

Речь идёт о незавершённых промисах, которые крайне сложно обнаружить. Извне нельзя узнать о том, остановился ли промис, или он просто медленно работает.

Авторы вышеупомянутой публикации показывают пример проблемы из node-promise-mysql, где connection.release() возвращает промис, который никогда не разрешается.

Этот пример сложно свести к нескольким строкам кода, поэтому вот — кое-что попроще:

Последний промис соединён цепочкой из .then() с промисом p0 , который никогда не разрешается и не отклоняется. Этот код может выполняться вечно, но он никогда не выведет никакого значения.

Это, опять же, простейший пример, но представьте себе огромную кодовую базу, над которой работают десятки программистов. Совсем неочевидным может быть то, что функция в некоторых случаях бесконечно долго держит некий промис в состоянии pending .

▍Паттерн №3: неявные возвраты и код, реагирующий на результаты работы промиса

Цепочка из промисов прерывается без выдачи каких-либо ошибок в том случае, если разработчик забывает о включении в код выражения return .

Эта проблема похожа на ту, связанную с «проглатыванием» ошибок, о которой я уже рассказывал. Вот фрагмент кода из Google Assistant, который приводят Алимадади с соавторами:

Читайте так же:
Постоянная синхронизация в itunes

Метод handleRequest() использует объект Map с обработчиками, предоставленными разработчиком, для организации асинхронной работы с запросами Assistant. Объект handler (обработчик) может быть либо коллбэком, либо промисом.

Если промис разрешается и вызывает предоставленный ему программистом анонимный обработчик, код возвращает результат. Если промис оказывается отклонённым — код возвращает причину этого.

Но все эти возвраты выполняются внутри кода, реагирующего на разрешение или отклонение промиса. Промис не осуществляет возврата значения. Результат реакции на промис handler теряется.

Пользователь этой библиотеки не может обработать результат разрешения или отклонения промисов, возвращённых его собственными обработчиками.

Поиск антипаттернов с помощью графа промисов

Алимадади с соавторами создали программу PromiseKeeper, которая динамически анализирует кодовую базу на JavaScript и рисует графы промисов.

Граф промисов

Мне не удалось запустить эту программу, поэтому передам то, о чём рассказали авторы публикации.

▍Графы промисов

Асинхронный код можно представить в виде графа, вершины которого (промисы, функции, значения, механизмы синхронизации) соединены рёбрами (разрешение промиса, регистрация обработчиков, связь, return или throw ).

  • Вершины-промисы (p) представляют собой случаи запуска промисов.
  • Вершины-значения (v) представляют значения, с которыми разрешаются или отклоняются промисы. Это могут быть функции.
  • Вершины-функции (f) — это функции, зарегистрированные для обработки разрешения или отклонения промиса.
  • Вершины-механизмы синхронизации (s) представляют собой все использованные в коде вызовы Promise.all() или Promise.race() .
  • Рёбра разрешения или отклонения промиса (v)->(p) указывают на связи вершин-значений с вершинами-промисами. Они помечены как resolve или reject .
  • Рёбра регистрации обработчиков (p)->(f) указывают на связи между промисом и функцией. Они помечены как onResolve или onReject .
  • Рёбра связей (p1)->(p2) показывают взаимоотношения между связанными промисами.
  • Рёбра return или throw (f)->(v) показывают связи функций и значений. Они помечены как return или throw .
  • Рёбра механизмов синхронизации указывают на связи, идущие от множества промисов к одному механизму синхронизации промисов. Они помечаются как resolved , rejected или pending на основании того, как ведёт себя новый промис.

Аннотированный граф промисов

▍Использование PromiseKeeper для поиска анти-паттернов

Программа PromiseKeeper направлена на конструирование и визуализацию графа промисов кода. Она анализирует код динамически, по мере выполнения тестов. Динамический контекст выполнения кода позволяет находить анти-паттерны, которые не видны при анализе самого кода.

Вот на какие анти-паттерны обращает внимание программа:

  • Пропущенные обработчики отклонения промисов — это ведёт к «проглатыванию» ошибок.
  • Попытки многократного завершения работы промисов — это происходит, когда пытаются разрешить или отклонить промис, работа которого уже была завершена.
  • Незавершённые промисы, то есть такие, которые не разрешены, но и не отклонены в то время, когда PromiseKeeper строит граф.
  • Недостижимые обработчики — то есть код, зарегистрированный для обработки разрешения или отклонения промисов, который не выполняется во время динамического анализа кода, выполняемого PromiseKeeper.
  • Неявные возвраты и стандартные обработчики — это может привести к неожиданному поведению промисов, расположенных ниже в цепочке промисов.
  • Ненужные промисы — когда намеренно создают новый промис в функции, которая уже обёрнута в промис.

Реализация PromiseKeeper основана на фреймворке для динамического анализа JavaScript-кода Jalangi. В нём имеются коллбэки, которые реагируют на события жизненного цикла промисов.

Мне не удалось заставить работать PromiseKeeper на моём компьютере, но Алимадади с соавторами сообщают о том, что не вполне благополучные промисы встречаются почти во всех кодовых базах, в которых используется JavaScript.

Отчёт по выявленным анти-паттернам

Интересно то, что 1012 экземпляров незавершённых промисов встречаются всего в 17 местах кода Node Fetch.

Авторы сообщили, что в ходе их эксперимента 43% промисов оказались неразрешёнными. Это, вероятнее всего, указывает на неполноту тестов, а не на то, что популярные программные продукты, которые они исследовали, безнадёжно «поломаны».

Что делать?

Для того чтобы улучшить работу с промисами в своих проектах, стоит помнить об анти-паттернах и постараться не писать код, который изначально является некачественным. Применение async/await снижает вероятность появления в коде рассмотренных здесь анти-паттернов.

Мы, решая проблемы промисов, добились серьёзных успехов, логируя сведения о необработанных отклонениях промисов, снабжённые полной трассировкой стека. Делается это с помощью такого кода:

Ещё можно попробовать Node.js-модуль async_hooks, который позволяет наблюдать за жизненным циклом промисов, и попытаться выявлять промисы, которые выполняются слишком долго. Можно, например, сравнивать время работы промиса с заданным тайм-аутом и выводить в консоль предупреждение.

У меня была интересная попытка использования async_hooks для выявления промисов, выполняющихся слишком долго, но особого толку из этого не вышло. Нельзя получить ссылку на контекст выполнения (только — C-указатель). То есть — можно увидеть, что что-то работает медленно, но о том, что это такое, узнать нельзя.

Меня привлекает идея превращения PromiseKeeper в нечто вроде плагина для Jest. Представьте себе, что после каждого запуска тестов формируется граф промисов, вроде того, который вы здесь видели.

Сталкивались ли вы с проблемами, вызванными неправильной работой промисов?

голоса
Рейтинг статьи
Ссылка на основную публикацию
Adblock
detector