На этой странице

У вас может быть проект, в котором есть долгий кусок C/C++-кода, который вы хотите выполнять в фоне, а не в главном event loop Node. Класс AsyncWorker из Node-API спроектирован именно для этого случая.

Как программисту, ваша задача по сути — создать подкласс AsyncWorker и реализовать метод Execute. Вы также, вероятно, реализуете функцию-обёртку, чтобы облегчить использование вашего AsyncWorker.

В этом примере мы создадим класс SimpleAsyncWorker, который является подклассом AsyncWorker. Воркер будет принимать целое значение, указывающее длительность времени, которое он должен «работать». Когда воркер завершится, он вернёт текстовую строку, указывающую, сколько он работал. В одном случае воркер вместо этого сообщит об ошибке.

SimpleAsyncWorker.h — C++-заголовок для SimpleAsyncWorker. Он объявляет конструктор, принимающий аргументом длительность времени (в секундах), которое SimpleAsyncWorker должен работать. Объявлен приватный член данных для хранения этого значения.

Заголовок также объявляет два метода, Execute и OnOK, которые переопределяют методы, объявленные в AsyncWorker, и подробнее описаны ниже.

SimpleAsyncWorker.cc — C++-реализация. Конструктор принимает два аргумента. callback — это JavaScript-функция, которая вызывается, когда метод Execute возвращается. callback вызывается независимо от того, была ошибка или нет. Второй аргумент конструктора, runTime, — целое значение, указывающее, как долго (в секундах) должен работать воркер.

Node будет выполнять код метода Execute в потоке, отдельном от потока, выполняющего главный event loop Node. Метод Execute не имеет доступа ни к какой части окружения Node-API. По этой причине входное значение runTime было сохранено конструктором как приватный член данных.

В этой реализации метод Execute просто ждёт число секунд, указанное ранее в runTime. Именно сюда в реальной реализации помещается долгий код. Чтобы продемонстрировать, как работает обработка ошибок, этот метод Execute объявляет ошибку, когда его просят проработать 4 секунды.

Метод OnOK вызывается после того, как метод Execute возвращается, — если только метод Execute не вызвал SetError или, в случае когда исключения C++ включены, не было выброшено исключение. В случае ошибки вместо OnOK вызывается метод OnError. Реализация OnError по умолчанию просто вызывает функцию-колбэк AsyncWorker с ошибкой в качестве единственного аргумента.

В этой реализации метод OnOK формирует строковое значение и передаёт его вторым аргументом в функцию callback, указанную в конструкторе. Первым аргументом, передаваемым в функцию callback, является JavaScript-значение null. Причина в том, что одна и та же функция-колбэк вызывается, произошла ошибка или нет. Метод OnError по умолчанию, который SimpleAsyncWorker не переопределяет, передаёт ошибку первым аргументом в колбэк. Это станет яснее в следующем разделе.

Обратите внимание, что, в отличие от Execute, методы OnOK и OnError имеют доступ к окружению Node-API.

Нам нужна C++-функция, которая инстанцирует объекты SimpleAsyncWorker и запрашивает постановку их в очередь. Эту функцию нужно зарегистрировать в Node-API, чтобы она была доступна из JavaScript-кода. RunSimpleAsyncWorker.cc предоставляет эту обёртку.

Функция runSimpleAsyncWorker, доступная из JavaScript, принимает два аргумента, передаваемых через аргумент info. Первый аргумент, передаваемый как info[0], — это runTime, а второй аргумент — JavaScript-функция-колбэк, которая вызывается, когда метод Execute возвращается.

Затем код инстанцирует объект SimpleAsyncWorker и запрашивает постановку его в очередь для возможного выполнения на следующем тике. Пока объект SimpleAsyncWorker не поставлен в очередь, его метод Execute никогда не будет вызван.

Как только объект SimpleAsyncWorker поставлен в очередь, runSimpleAsyncWorker формирует текстовую строку и возвращает её вызывающей стороне.

Test.js — простая JavaScript-программа, показывающая, как запускать экземпляры SimpleAsyncWorker. Она вызывает runSimpleAsyncWorker три раза, каждый раз с другим параметром runTime. Каждый вызов указывает AsyncWorkerCompletion как функцию-колбэк.

Функция AsyncWorkerCompletion написана так, чтобы обрабатывать случаи, когда метод Execute сообщает об ошибке и когда нет. Она просто логирует в консоль при вызове.

Вот как выглядит вывод, когда JavaScript успешно отрабатывает:

runSimpleAsyncWorker returned 'SimpleAsyncWorker for 2 seconds queued.'.
runSimpleAsyncWorker returned 'SimpleAsyncWorker for 4 seconds queued.'.
runSimpleAsyncWorker returned 'SimpleAsyncWorker for 8 seconds queued.'.
SimpleAsyncWorker returned 'SimpleAsyncWorker returning after 'working' 2 seconds.'.
SimpleAsyncWorker returned an error:  [Error: Oops! Failed after 'working' 4 seconds.]
SimpleAsyncWorker returned 'SimpleAsyncWorker returning after 'working' 8 seconds.'.

Как и ожидалось, каждый вызов runSimpleAsyncWorker немедленно возвращается. Функция AsyncWorkerCompletion вызывается, когда каждый SimpleAsyncWorker завершается.

  • Абсолютно существенно, чтобы метод Execute не делал никаких вызовов Node-API. Это значит, что метод Execute не имеет доступа ни к каким входным значениям, переданным JavaScript-кодом.

    Обычно конструктор класса AsyncWorker собирает нужную ему информацию из JavaScript-объектов и хранит копии этой информации как члены данных. Результаты метода Execute затем можно превратить обратно в JavaScript-объекты в методе OnOK.

  • Процесс Node знает обо всех работающих методах Execute и не завершится, пока все работающие методы Execute не вернутся.

  • AsyncWorker можно безопасно завершить вызовом AsyncWorker::Cancel из главного потока.