Spec-Zone.ru › Q

Q

Методы промисов

У большинства методов промисов есть «статические» аналоги у основного объекта Q. Они принимают промис или значение, не являющееся промисом; во втором случае сначала создаётся исполненный промис. Например, Q.when(5, onFulfilled) эквивалентен Q(5).then(onFulfilled). У всех остальных методов есть статические аналоги с такими же именами, как у методов промисов.

Некоторые методы называются так же, как зарезервированные слова JavaScript, например try, catch и finally. Это позволяет наглядно показать очевидную параллель между стандартными синхронными языковыми конструкциями и асинхронными операциями с промисами. Однако использование таких слов в качестве имён свойств поддерживается только начиная с редакции ECMAScript 5 языка JavaScript, которая не реализована в некоторых старых браузерах, таких как IE8, Safari 5, Android 2.2 и PhantomJS 1.8. Если вы ориентируетесь на эти браузеры и не используете язык вроде CoffeeScript, который решает эту проблему за вас, используйте соответствующие псевдонимы или экранируйте их, например так: Q["try"](...) или promise["catch"](...).

Основные методы промисов

promise.then(onFulfilled, onRejected, onProgress)

Метод then из спецификации Promises/A+ с дополнительным обработчиком прогресса.

promise.catch(onRejected)

Псевдоним: promise.fail (для браузеров без поддержки ES5)

Вспомогательный метод, эквивалентный promise.then(undefined, onRejected).

promise.progress(onProgress)

Устарел: в версии 2 этот метод должен быть заменён на promise.observeEstimate или аналогичный интерфейс. Прогресс плохо сочетается с композицией. https://github.com/kriskowal/gtor#progress-and-estimated-time-to-completion

Вспомогательный метод, эквивалентный promise.then(undefined, undefined, onProgress). Обработчик onProgress получает значения, отправленные этому промису либо методом notify соответствующего отложенного объекта, либо промисом, которым стал этот промис в результате возврата из обработчика.

promise.finally(callback)

Псевдоним: promise.fin (для браузеров без поддержки ES5)

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

finally возвращает промис, который будет разрешён с тем же значением исполнения или причиной отклонения, что и promise. Однако если callback возвращает промис, разрешение возвращённого промиса будет отложено до завершения промиса, возвращённого из callback. Кроме того, если возвращённый промис будет отклонён, это отклонение передастся дальше по цепочке вместо предыдущего результата.

promise.done(onFulfilled, onRejected, onProgress)

Похож на then, но иначе обрабатывает необработанные отклонения. Если отклонение осталось необработанным — либо потому, что promise был отклонён и обработчик onRejected не был указан, либо потому, что onFulfilled или onRejected выбросил ошибку или вернул отклонённый промис, — причина итогового отклонения будет выброшена как исключение на следующем цикле событий.

Этот метод следует использовать для завершения цепочек промисов, которые не будут передаваться дальше. Поскольку исключения, выброшенные в обратных вызовах then, перехватываются и преобразуются в отклонения, исключения в конце цепочки легко случайно и незаметно проигнорировать. Если организовать выброс исключения на следующем цикле событий, чтобы его нельзя было перехватить, в браузере будет вызвано событие onerror у объекта window, а в Node.js — событие uncaughtException у объекта process.

У исключений, выброшенных методом done, будут длинные трассировки стека, если Q.longStackSupport установлен в true. Если задан Q.onerror, исключения будут передаваться туда, а не выбрасываться на следующем цикле событий.

Золотое правило использования done и then таково: либо передайте свой промис кому-нибудь ещё с помощью return, либо, если цепочка заканчивается на вас, завершите её вызовом done. Завершить её с помощью catch недостаточно, поскольку сам обработчик catch может выбросить ошибку.

Методы для промисов объектов

promise.get(propertyName)

Возвращает промис, который выдаст именованное свойство объекта. По сути, эквивалентен следующему коду:

promise.then(function (o) {
    return o[propertyName];
});

promise.post(methodName, args)

Экспериментальный псевдоним: promise.mapply

Возвращает промис с результатом вызова именованного метода объекта с заданным массивом аргументов. Сам объект передаётся функции как this, как и при синхронном вызове метода. По сути, эквивалентен следующему коду:

promise.then(function (o) {
    return o[methodName].apply(o, args);
});

promise.invoke(methodName, ...args)

Псевдоним: promise.send

Экспериментальный псевдоним: promise.mcall

Возвращает промис с результатом вызова именованного метода объекта с заданными аргументами переменной длины. Сам объект передаётся функции как this, как и при синхронном вызове метода.

promise.keys()

Возвращает промис с массивом имён свойств объекта. По сути, эквивалентен следующему коду:

promise.then(function (o) {
    return Object.keys(o);
});

Методы для промисов функций

promise.fbind(...args) (устарел)

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

Этот метод особенно полезен в статической форме для обёртывания функций, чтобы гарантировать, что они всегда выполняются асинхронно, а любые выброшенные исключения (преднамеренные или случайные) корректно преобразуются в возвращённый отклонённый промис. Например:

var getUserData = Q.fbind(function (userName) {
    if (!userName) {
        throw new Error("userName must be truthy!");
    }

    if (localCache.has(userName)) {
        return localCache.get(userName);
    }

    return getUserFromCloud(userName);
});

promise.fapply(args)

Возвращает промис с результатом вызова функции с заданным массивом аргументов. По сути, эквивалентен следующему коду:

promise.then(function (f) {
    return f.apply(undefined, args);
});

Обратите внимание: в результате будет выполнено такое же преобразование возвращаемого значения или выброшенного исключения, как описано выше для fbind.

promise.fcall(...args)

Статический псевдоним: Q.try (только для браузеров с поддержкой ES5)

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

В статической форме метод имеет псевдоним Q.try, поскольку его семантика схожа с блоком try (при этом обрабатываются как синхронные исключения, так и асинхронные отклонения). Благодаря этому можно писать код вроде следующего:

Q
.try(function () {
    if (!isConnectedToCloud()) {
        throw new Error("The cloud is down!");
    }

    return syncToCloud();
})
.catch(function (error) {
    console.error("Couldn't sync to the cloud", error);
});

Методы для промисов массивов

promise.all()

Возвращает промис, который исполняется с массивом, содержащим значения исполнения каждого промиса, или отклоняется с той же причиной, что и первый отклонённый промис.

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

Q.all([getFromDisk(), getFromCloud()]).done(function (values) {
    assert(values[0] === values[1]); // values[0] is fromDisk and values[1] is fromCloud
});

promise.allSettled()

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

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

Q.allSettled([saveToDisk(), saveToCloud()]).spread(function (disk, cloud) {
    console.log("saved to disk:", disk.state === "fulfilled");
    console.log("saved to cloud:", cloud.state === "fulfilled");
}).done();

Снимки состояния будут иметь тот же формат, что и снимки, полученные с помощью promise.inspect, то есть будут иметь вид { state: "fulfilled", value: v } или { state: "rejected", reason: r }.

promise.spread(onFulfilled, onRejected)

Подобен then, но «распаковывает» массив и передаёт его элементы обработчику исполнения в виде аргументов переменной длины. Если какой-либо промис в массиве отклонён, вместо этого вызывает onRejected с причиной отклонения первого отклонённого промиса.

Особенно полезен в сочетании с all, например:

Q.all([getFromDisk(), getFromCloud()]).spread(function (diskVal, cloudVal) {
    assert(diskVal === cloudVal);
}).done();

Вспомогательные методы

promise.thenResolve(value)

Статического аналога нет

Вспомогательный метод, эквивалентный promise.then(function () { return value; }).

promise.thenReject(reason)

Статического аналога нет

Вспомогательный метод, эквивалентный promise.then(function () { throw reason; }).

promise.tap(onFulfilled)

Добавлен в версии 1.1.0 (ноябрь 2014 г.)

Добавляет обработчик, который наблюдает за значением промиса после его исполнения и возвращает промис с тем же значением. Возвращённый промис может быть отложен, но не заменяется промисом, возвращённым обработчиком onFulfilled.

Q("Hello, World!")
.delay(1000)
.tap(console.log)
.then(function (message) {
    expect(message).toBe("Hello, World!");
})

promise.timeout(ms, message)

Возвращает промис с тем же результатом, что и promise, за исключением случая, когда promise не будет исполнен или отклонён до истечения ms миллисекунд. В этом случае возвращённый промис будет отклонён с объектом Error и указанным message. Если message не задан, сообщение будет "Timed out after " + ms + " ms".

promise.timeout(10000).then(
  function (result) {
    // will be called if the promise resolves normally
    console.log(result);
  },
  function (err) {
    // will be called if the promise is rejected, or the 10 second timeout occurs
    console.log(err);
  }
);

promise.delay(ms)

Возвращает промис с тем же результатом, что и promise, но он будет исполнен только после того, как пройдёт не менее ms миллисекунд. Если promise будет отклонён, результирующий промис будет отклонён немедленно.

Q.delay(ms)

Если статической версии Q.delay передан только один аргумент, она возвращает промис, который будет исполнен со значением undefined после того, как пройдёт не менее ms миллисекунд. (Если переданы два аргумента, используется обычное преобразование для статического аналога, то есть Q.delay(value, ms) эквивалентен Q(value).delay(ms).)

Это удобный способ добавить задержку в цепочку промисов или просто получить более удобный синтаксис для setTimeout:

Q.delay(150).then(doSomething);

Методы проверки состояния

promise.isFulfilled()

Возвращает признак того, что данный промис находится в состоянии исполнения. При использовании статической версии с аргументом, не являющимся промисом, результат всегда true.

promise.isRejected()

Возвращает признак того, что данный промис находится в состоянии отклонения. При использовании статической версии с аргументом, не являющимся промисом, результат всегда false.

promise.isPending()

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

promise.inspect()

Возвращает объект «снимка состояния», который может иметь один из трёх видов:

  • { state: "pending" }
  • { state: "fulfilled", value: <fulfllment value> }
  • { state: "rejected", reason: <rejection reason> }

Создание промисов

Q.defer()

Возвращает объект «отложенного», содержащий:

  • свойство promise
  • метод resolve(value)
  • метод reject(reason)
  • метод notify(value)
  • метод makeNodeResolver()

Методы resolve и reject управляют состоянием свойства promise. Его можно передавать другим, сохраняя за собой право изменять его состояние. Метод notify предназначен для уведомления о прогрессе, а метод makeNodeResolver — для взаимодействия с Node.js (см. ниже).

Во всех случаях, когда промис разрешён (то есть исполнен или отклонён), его разрешённое состояние становится окончательным и не может быть сброшено. Попытки вызвать resolve, reject или notify, если promise уже разрешён, ничего не дадут.

Отложенные объекты удобны, поскольку отделяют промис от объекта, который им управляет. Поэтому:

  • Вы можете передать промис любому количеству потребителей, и каждый из них независимо увидит результат его разрешения. Поскольку возможность наблюдать за промисом отделена от возможности разрешать его, получатели промиса не могут «обмануть» друг друга дезинформацией (или каким-либо образом помешать друг другу).

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

deferred.resolve(value)

Вызов resolve с ожидающим промисом заставляет promise ожидать переданный промис. Затем он будет исполнен со значением исполнения переданного промиса или отклонён с причиной его отклонения (либо останется в ожидании навсегда, если так и произойдёт с переданным промисом).

Вызов resolve с отклонённым промисом приводит к тому, что promise отклоняется с причиной отклонения переданного промиса.

Вызов resolve с исполненным промисом приводит к тому, что promise исполняется со значением исполнения переданного промиса.

Вызов resolve со значением, не являющимся промисом, приводит к тому, что promise исполняется с этим значением.

deferred.reject(reason)

Вызов reject с причиной приводит к тому, что promise отклоняется с этой причиной.

deferred.notify(value)

Вызов notify со значением приводит к тому, что promise получает уведомление о прогрессе с этим значением. То есть все обработчики onProgress, зарегистрированные с помощью promise, или унаследованные промисы, созданные на основе promise, будут вызваны со значением прогресса.

Объект Q

Q(value)

Если value — это промис Q, возвращает этот промис.

Если value — это промис из другой библиотеки, он преобразуется в промис Q (если это возможно).

Если value не является промисом, возвращает промис, исполненный со значением value.

Q.reject(reason)

Возвращает промис, отклонённый с причиной reason.

Q.Promise(resolver)

Синхронно вызывает resolver(resolve, reject, notify) и возвращает промис, состояние которого управляется функциями, переданными в resolver. Это альтернативный API для создания промисов, который обладает теми же возможностями, что и концепция отложенного объекта, но не вводит дополнительную сущность.

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

примечание: В последней версии на GitHub этот метод называется Q.Promise, но в npm-пакете версии 0.9.7 или ниже он называется Q.promise (строчная или заглавная буква p).

Взаимодействие с обратными вызовами Node.js

В Q есть несколько функций для взаимодействия с API, использующими обратные вызовы в стиле Node.js (err, result).

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

Обратите внимание: если API в стиле Node.js вызывает обратный вызов с несколькими параметрами, не являющимися ошибками (например, child_process.execFile), при преобразовании Q упаковывает их в массив, который становится значением исполнения промиса.

Q.nfbind(nodeFunc, ...args)

Псевдоним: Q.denodeify

Создаёт функцию, возвращающую промис, на основе функции в стиле Node.js; при необходимости привязывает к ней заданные аргументы переменной длины. Пример:

var readFile = Q.nfbind(FS.readFile);

readFile("foo.txt", "utf-8").done(function (text) {

});

Обратите внимание: если у вас есть метод, использующий шаблон обратных вызовов Node.js, а не просто функция, перед передачей его в nfbind необходимо привязать значение this, например так:

var Kitty = mongoose.model("Kitty");
var findKitties = Q.nfbind(Kitty.find.bind(Kitty));

Для методов лучше использовать Q.nbind, как показано ниже.

Q.nbind(nodeMethod, thisArg, ...args)

Создаёт функцию, возвращающую промис, на основе метода в стиле Node.js; при необходимости привязывает к ней заданные аргументы переменной длины. Пример:

var Kitty = mongoose.model("Kitty");
var findKitties = Q.nbind(Kitty.find, Kitty);

findKitties({ cute: true }).done(function (theKitties) {

});

Q.nfapply(nodeFunc, args)

Вызывает функцию в стиле Node.js с заданным массивом аргументов и возвращает промис, который исполняется, если функция Node.js вызывает обратный вызов с результатом, или отклоняется, если обратный вызов вызывается с ошибкой (либо ошибка выбрасывается синхронно). Пример:

Q.nfapply(FS.readFile, ["foo.txt", "utf-8"]).done(function (text) {
});

Обратите внимание: этот пример работает только потому, что FS.readFile — это функция, экспортированная из модуля, а не метод объекта. Для методов, например redisClient.get, перед передачей в Q.nfapply (или, в общем случае, в качестве аргумента любого вызова функции) необходимо привязать метод к экземпляру:

Q.nfapply(redisClient.get.bind(redisClient), ["user:1:id"]).done(function (user) {
});

Для методов лучше использовать Q.npost, как показано ниже.

Q.nfcall(func, ...args)

Вызывает функцию в стиле Node.js с заданными аргументами переменной длины и возвращает промис, который исполняется, если функция Node.js вызывает обратный вызов с результатом, или отклоняется, если обратный вызов вызывается с ошибкой (либо ошибка выбрасывается синхронно). Пример:

Q.nfcall(FS.readFile, "foo.txt", "utf-8").done(function (text) {
});

Для nfcall действует то же предупреждение о различиях между функциями и методами, что и для nfapply. В этом случае лучше использовать Q.ninvoke.

Q.npost(object, methodName, args)

Устаревший псевдоним: Q.nmapply

Вызывает метод в стиле Node.js с заданным массивом аргументов и возвращает промис, который исполняется, если метод вызывает обратный вызов с результатом, или отклоняется, если обратный вызов вызывается с ошибкой (либо ошибка выбрасывается синхронно). Пример:

Q.npost(redisClient, "get", ["user:1:id"]).done(function (user) {
});

Q.ninvoke(object, methodName, ...args)

Псевдоним: Q.nsend

Устаревший псевдоним: Q.nmcall

Вызывает метод в стиле Node.js с заданными аргументами переменной длины и возвращает промис, который исполняется, если метод вызывает обратный вызов с результатом, или отклоняется, если обратный вызов вызывается с ошибкой (либо ошибка выбрасывается синхронно). Пример:

Q.ninvoke(redisClient, "get", "user:1:id").done(function (user) {
});

promise.nodeify(callback)

Если callback — функция, она считается обратным вызовом в стиле Node.js и вызывается как callback(rejectionReason), если promise будет отклонён, или как callback(null, fulfillmentValue), если promise будет исполнен. Если callback не является функцией, просто возвращает promise.

Этот метод полезен для создания API, поддерживающих и промисы, и обратные вызовы, то есть API, которые возвращают промисы, но также принимают обратные вызовы в стиле Node.js. Например:

function createUser(userName, userData, callback) {
    return database.ensureUserNameNotTaken(userName)
    .then(function () {
        return database.saveUserData(userName, userData);
    })
    .nodeify(callback);
}

deferred.makeNodeResolver()

Возвращает функцию, подходящую для передачи в API Node.js. Она имеет сигнатуру (err, result) и отклонит deferred.promise с err, если задан err, или исполнит его со значением result, если задано оно.

Генераторы

Эта функция экспериментальная.

Q.async(generatorFunction)

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

Q.spawn(generatorFunction)

Немедленно запускает функцию-генератор и передаёт все неперехваченные ошибки в Q.onerror. Ошибка считается неперехваченной, если функция возвращает отклонённый промис. Обратите внимание: это происходит автоматически, если функция-генератор выбрасывает ошибку, например при выполнении yield для промиса, который отклоняется, если не заключить этот код в try/catch:

Q.spawn(function* () {
    // If `createUser` returns a rejected promise, the rejection reason will
    // reach `Q.onerror`.
    var user = yield createUser();
    showUserInUI(user);
});

Обработка ошибок и отслеживание

Q.onerror

Настраиваемое свойство, которое перехватывает любые необработанные ошибки, иначе выбрасываемые на следующем цикле событий, обычно в результате done. Может быть полезно для получения полной трассировки стека ошибки в браузерах, что обычно невозможно с помощью window.onerror.

Q.getUnhandledReasons()

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

Q.stopUnhandledRejectionTracking()

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

Q.resetUnhandledRejections()

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

Прочее

Q.isPromise(value)

Возвращает значение, указывающее, является ли переданное значение промисом Q.

Q.isPromiseAlike(value)

Возвращает значение, указывающее, является ли переданное значение промисом (то есть объектом с функцией then).

Q.promised(func)

Создаёт новую версию func, принимающую любые сочетания значений-промисов и обычных значений и преобразующую их в значения, которыми промисы завершаются успешно, прежде чем вызвать исходную func. Возвращённая версия также всегда возвращает промис: если func выполняет return или throw, то Q.promised(func) вернёт успешно завершённый или отклонённый промис соответственно.

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

Q.longStackSupport

Свойство, которому можно присвоить значение, чтобы включить поддержку длинных трассировок стека. Если она включена, «переходы стека» отслеживаются в асинхронных операциях с промисами, поэтому, если done выбрасывает необработанную ошибку или в обработчике отклонения проверяется свойство stack причины отклонения, формируется длинная трассировка стека.

API пользовательских сообщений (дополнительно)

Конструктор промисов Q задаёт базовый API для выполнения операций над объектами: «get», «put», «del», «post», «apply» и «keys». Этот набор «операторов» можно расширить, создавая промисы, которые отвечают на сообщения с другими именами операторов, и отправляя этим промисам соответствующие сообщения.

promise.dispatch(operator, args)

Отправляет промису произвольное сообщение с указанным массивом аргументов.

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

© 2009–2018 Kristopher Michael Kowal
Licensed under the MIT License.
https://github.com/kriskowal/q/wiki/API-Reference

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API