В лучшем случае вы можете проверить .thenметод, но это не скажет вам, что то, что у вас есть, является Обещанием окончательно. Все, что вы знаете в этот момент, - это то, что у вас есть что-то, что раскрывает .thenметод, например, Обещание.
Скотт Оффен
@ScottOffen спецификация обещания явно не делает различий.
Бенджамин Грюнбаум
6
Я .thenхочу сказать, что любой может создать объект, который предоставляет метод, который не является Обещанием, не ведет себя как Обещание и не имеет намерения использоваться как Обещание. Проверка на .thenметод просто говорит вам , что если объект не имеет .thenметод, то вы не имеете обещание. Обратное - что существование .thenметода означает , что вы делаете есть обещание - это не всегда верно.
Скотт Оффен
3
@ScottOffen По определению, единственный установленный способ идентифицировать обещание - проверить, есть ли у него .thenметод. Да, это может привести к ложным срабатываниям, но это предположение, что все библиотеки обещаний полагаются (потому что это все, на что они могут положиться). Насколько я вижу, единственная альтернатива - это принять предложение Бенджамина Грюнбаума и провести его через тестовый набор обещаний. Но это не практично для реального производственного кода.
JLRishe
Ответы:
342
Как решает библиотека обещаний
Если у него есть .thenфункция - это единственное стандартное обещание, которое используют библиотеки.
В спецификации Promises / A + есть понятие, называемое thenable, которое по сути является «объектом с thenметодом». Обещания будут и должны ассимилировать что-либо с помощью метода then. Все реализации обещаний, которые вы упомянули, делают это.
2.3.3.3 Если thenэто функция, вызовите ее с x как this, первый аргумент resolPromise и второй аргумент rejectPromise
Это также объясняет обоснование этого проектного решения:
Такое обращение с thenвозможностями позволяет взаимодействиям обещаний взаимодействовать, если они предоставляют thenспособ, соответствующий Promises / A + . Это также позволяет реализациям Promises / A + «ассимилировать» несовместимые реализации разумными методами then.
Как вы должны решить
Вы не должны - вместо этого вызывать Promise.resolve(x)( Q(x)в Q), который всегда преобразует любое значение или внешнее thenсостояние в доверенное обещание. Это безопаснее и проще, чем выполнять эти проверки самостоятельно.
действительно нужно быть уверенным?
Вы всегда можете запустить его через набор тестов : D
так Promise.resolve может справиться с чем угодно ? Конечно, нет ничего, но я думаю, что-нибудь разумное?
Александр Миллс
3
@AlexMills да, это работает даже для нестандартных обещаний, таких как обещание jQuery. Он может потерпеть неудачу, если у объекта есть метод then, интерфейс которого полностью отличается от обещания then.
Esailija
19
Этот ответ, хотя, возможно, хороший совет, на самом деле не отвечает на вопрос.
Стейн де Витт
4
Если вопрос действительно не о том, кто-то на самом деле реализует библиотеку обещаний, вопрос недействителен. Только библиотека обещаний должна будет выполнить проверку, после этого вы всегда можете использовать ее метод .resolve, как я показал.
Esailija
4
@Esalija Вопрос представляется мне актуальным и важным, а не только для исполнителя библиотеки обещаний. Это также относится к пользователю библиотеки обещаний, который хочет знать, как будут / должны / могут вести себя реализации, и как разные библиотеки обещаний будут взаимодействовать друг с другом. В частности, этот пользователь сильно встревожен тем очевидным фактом, что я могу дать обещание X для любого X, кроме случаев, когда X означает «обещание» (что бы здесь ни значило «обещание» - вот в чем вопрос), и я определенно заинтересован точно знать, где лежат границы этого исключения.
Дон Хэтч
104
Вот мой оригинальный ответ, который с тех пор был утвержден в спецификации как способ проверки на обещание:
Promise.resolve(obj)== obj
Это работает, потому что алгоритм явно требует, Promise.resolveчтобы он возвращал точный переданный объект, если и только если это обещание по определению спецификации.
У меня здесь есть другой ответ, который раньше говорил это, но я изменил его на что-то другое, когда он не работал с Safari в то время. Это было год назад, и теперь это надежно работает даже в Safari.
Я бы отредактировал свой первоначальный ответ, за исключением того, что он был ошибочным, учитывая, что к настоящему моменту за это решение проголосовало больше людей, чем за оригинал. Я считаю, что это лучший ответ, и я надеюсь, что вы согласны.
Это также не сработает для обещаний, которые не относятся к той же сфере.
Бенджамин Грюнбаум
4
«обещание по определению спецификации», по-видимому, означает «обещание, созданное тем же конструктором, что и обещание, созданное с помощью Promise.resolve ()» - так что это не удастся обнаружить, например, если. Обещание, заполненное полицией, на самом деле является Обещанием
VoxPelli
3
Этот ответ мог бы быть улучшен, если бы он начинался с указания того, как вы интерпретируете вопрос, а не сразу с ответа - ОП, к сожалению, вообще не прояснил его, и вы тоже этого не сделали, поэтому на данный момент ОП, писатель и читатель, скорее всего, находятся на 3 разных страницах. Документ, на который вы ссылаетесь, гласит: «Если аргумент является обещанием, созданным этим конструктором », выделенная курсивом часть является решающей. Было бы хорошо заявить, что на этот вопрос вы отвечаете. Также, что ваш ответ полезен для пользователя этой библиотеки, но не для разработчика.
Обновление: это больше не лучший ответ. Пожалуйста, проголосуйте за мой другой ответ .
obj instanceofPromise
должен сделать это. Обратите внимание, что это может надежно работать только с собственными обещаниями es6.
Если вы используете прокладку, библиотеку обещаний или что-то еще, притворяющееся подобным обещанию, тогда может быть более уместно проверить «жизнеспособность» (что-нибудь с .thenметодом), как показано в других ответах здесь.
С тех пор мне было указано, что не Promise.resolve(obj) == objбудет работать в Safari. Используйте instanceof Promiseвместо этого.
Джиб
2
Это не работает надежно и вызвало у меня невероятно сложную проблему. Скажем, у вас есть библиотека, которая использует прокладку es6.promise, и вы где-то используете Bluebird, у вас будут проблемы. Эта проблема возникла у меня в Chrome Canary.
Вон
1
Да, этот ответ на самом деле неправильный. Я попал сюда из-за такой сложной проблемы. Вы действительно должны проверить obj && typeof obj.then == 'function'вместо этого, потому что он будет работать со всеми типами обещаний и фактически является способом, рекомендованным спецификацией и используемым реализациями / полифиллами. Promise.allНапример, Native будет работать на всех thenспособностях, а не только на других обещаниях. Так должен ваш код. Так что instanceof Promiseэто не очень хорошее решение.
Стейн де Витт
2
Followup - это хуже: На node.js 6.2.2 , используя только собственные обещания , я сейчас пытаюсь отлаживать проблему , где console.log(typeof p, p, p instanceof Promise);производит этот выход: object Promise { <pending> } false. Как вы можете видеть, это обещание хорошо - и все же instanceof Promiseтест возвращается false?
Мёрре
2
Это не удастся для обещаний, которые не относятся к той же сфере.
Бенджамин Грюнбаум
46
if(typeof thing.then ==='function'){// probably a promise}else{// definitely not a promise}
что если вещь не определена? вам нужно остерегаться этого с помощью вещи && ...
mrBorna
не самый лучший, но вполне вероятно; зависит также от масштаба проблемы. Написание 100% защиты обычно применимо в открытых открытых API-интерфейсах или в тех случаях, когда вы знаете, что форма / сигнатура данных полностью открыта.
rob2d
17
Чтобы увидеть, является ли данный объект Обещанием ES6 , мы можем использовать этот предикат:
function isPromise(p){return p &&Object.prototype.toString.call(p)==="[object Promise]";}
CallING toStringнепосредственно от Object.prototypeвозвращения в родную строковое представление данного типа объекта , который "[object Promise]"в нашем случае. Это гарантирует, что данный объект
Обходит ложные срабатывания, такие как ..:
Определенный пользователем тип объекта с тем же именем конструктора («Обещание»).
Самописный toStringметод данного объекта.
Работает в разных контекстах среды (например, в фреймах) в отличие отinstanceof или isPrototypeOf.
Однако любой конкретный хост-объект , тег которого был изменен с помощьюSymbol.toStringTag , может вернуться "[object Promise]". Это может быть ожидаемый результат или нет в зависимости от проекта (например, если есть пользовательская реализация Promise).
Чтобы увидеть, является ли объект родным ES6 Promise , мы можем использовать:
function isNativePromise(p){return p &&typeof p.constructor==="function"&&Function.prototype.toString.call(p.constructor).replace(/\(.*\)/,"()")===Function.prototype.toString.call(/*native object*/Function).replace("Function","Promise")// replacing Identifier.replace(/\(.*\)/,"()");// removing possible FormalParameterList }
Согласно этому и этому разделу спецификации, строковое представление функции должно быть:
" Идентификатор функции ( FormalParameterList opt ) { FunctionBody }"
который обрабатывается соответственно выше. FunctionBody находится [native code]во всех основных браузерах.
Не ответ на полный вопрос, но я думаю, что стоит упомянуть, что в Node.js 10 isPromiseбыла добавлена новая вызываемая функция, которая проверяет, является ли объект собственным Promise или нет:
Как и в этом ответе , любой объект может иметь метод «затем» и, следовательно, не всегда может рассматриваться как обещание.
Богьон Хоффманн
6
Если вы используете Typescript , я хотел бы добавить, что вы можете использовать функцию «предиката типа». Просто следует обернуть логическую проверку в функцию, которая возвращает, x is Promise<any>и вам не нужно будет делать типы типов. Ниже на моем примере cприведено либо обещание, либо один из моих типов, которые я хочу преобразовать в обещание путем вызова c.fetch()метода.
exportfunction toPromise(c:Container<any>|Promise<any>):Promise<any>{if(c ==null)returnPromise.resolve();return isContainer(c)? c.fetch(): c;}exportfunction isContainer(val:Container<any>|Promise<any>): val is Container<any>{return val &&(<Container<any>>val).fetch !==undefined;}exportfunction isPromise(val:Container<any>|Promise<any>): val is Promise<any>{return val &&(<Promise<any>>val).then !==undefined;}
Если функция возвращает обещание, она будет ожидать и возвращаться с разрешенным значением. Если функция возвращает значение, оно будет считаться разрешенным.
Если функция не возвращает обещание сегодня, но завтра возвращает одно или объявляется как асинхронное, вы будете ориентированы на будущее.
это работает, согласно здесь : «если [ожидало] значение не обещание, то [Await выражение] преобразует значение разрешенного Promise, и ждет его»
pqnet
Это в основном то, что было предложено в принятом ответе, за исключением того, что здесь используется синтаксис async-await вместоPromise.resolve()
B12Toaster
3
it('should return a promise',function(){var result = testedFunctionThatReturnsPromise();
expect(result).toBeDefined();// 3 slightly different ways of verifying a promise
expect(typeof result.then).toBe('function');
expect(result instanceofPromise).toBe(true);
expect(result).toBe(Promise.resolve(result));});
если вы Promiseсоздадите подкласс и создадите его экземпляры, этот тест может не пройти. это должно работать для большей части того, что вы пытаетесь проверить, хотя.
Терама
Согласен, но я не понимаю, зачем кому-то создавать подклассы обещаний
Себастьен Х.
fn.constructor.name === 'AsyncFunction'это неправильно - это означает, что что-то является асинхронной функцией, а не обещанием - также не гарантируется, что люди могут выполнять обещания подкласса
Бенджамин Грюнбаум
@BenjaminGruenbaum Приведенный выше пример работает в большинстве случаев, если вы создаете свой собственный подкласс, вы должны добавить тесты для его имени
Себастьен Х.
Вы можете, но если вы уже знаете, какие объекты есть, вы уже знаете, обещания или нет.
.then
метод, но это не скажет вам, что то, что у вас есть, является Обещанием окончательно. Все, что вы знаете в этот момент, - это то, что у вас есть что-то, что раскрывает.then
метод, например, Обещание..then
хочу сказать, что любой может создать объект, который предоставляет метод, который не является Обещанием, не ведет себя как Обещание и не имеет намерения использоваться как Обещание. Проверка на.then
метод просто говорит вам , что если объект не имеет.then
метод, то вы не имеете обещание. Обратное - что существование.then
метода означает , что вы делаете есть обещание - это не всегда верно..then
метод. Да, это может привести к ложным срабатываниям, но это предположение, что все библиотеки обещаний полагаются (потому что это все, на что они могут положиться). Насколько я вижу, единственная альтернатива - это принять предложение Бенджамина Грюнбаума и провести его через тестовый набор обещаний. Но это не практично для реального производственного кода.Ответы:
Как решает библиотека обещаний
Если у него есть
.then
функция - это единственное стандартное обещание, которое используют библиотеки.В спецификации Promises / A + есть понятие, называемое
then
able, которое по сути является «объектом сthen
методом». Обещания будут и должны ассимилировать что-либо с помощью метода then. Все реализации обещаний, которые вы упомянули, делают это.Если мы посмотрим на спецификации :
Это также объясняет обоснование этого проектного решения:
Как вы должны решить
Вы не должны - вместо этого вызывать
Promise.resolve(x)
(Q(x)
в Q), который всегда преобразует любое значение или внешнееthen
состояние в доверенное обещание. Это безопаснее и проще, чем выполнять эти проверки самостоятельно.действительно нужно быть уверенным?
Вы всегда можете запустить его через набор тестов : D
источник
Проверка того, что что-то обещает излишне усложняет код, просто используйте
Promise.resolve
источник
Вот мой оригинальный ответ, который с тех пор был утвержден в спецификации как способ проверки на обещание:
Это работает, потому что алгоритм явно требует,
Promise.resolve
чтобы он возвращал точный переданный объект, если и только если это обещание по определению спецификации.У меня здесь есть другой ответ, который раньше говорил это, но я изменил его на что-то другое, когда он не работал с Safari в то время. Это было год назад, и теперь это надежно работает даже в Safari.
Я бы отредактировал свой первоначальный ответ, за исключением того, что он был ошибочным, учитывая, что к настоящему моменту за это решение проголосовало больше людей, чем за оригинал. Я считаю, что это лучший ответ, и я надеюсь, что вы согласны.
источник
===
вместо==
?Обновление: это больше не лучший ответ. Пожалуйста, проголосуйте за мой другой ответ .
должен сделать это. Обратите внимание, что это может надежно работать только с собственными обещаниями es6.
Если вы используете прокладку, библиотеку обещаний или что-то еще, притворяющееся подобным обещанию, тогда может быть более уместно проверить «жизнеспособность» (что-нибудь с
.then
методом), как показано в других ответах здесь.источник
Promise.resolve(obj) == obj
будет работать в Safari. Используйтеinstanceof Promise
вместо этого.obj && typeof obj.then == 'function'
вместо этого, потому что он будет работать со всеми типами обещаний и фактически является способом, рекомендованным спецификацией и используемым реализациями / полифиллами.Promise.all
Например, Native будет работать на всехthen
способностях, а не только на других обещаниях. Так должен ваш код. Так чтоinstanceof Promise
это не очень хорошее решение.console.log(typeof p, p, p instanceof Promise);
производит этот выход:object Promise { <pending> } false
. Как вы можете видеть, это обещание хорошо - и все жеinstanceof Promise
тест возвращаетсяfalse
?источник
Чтобы увидеть, является ли данный объект Обещанием ES6 , мы можем использовать этот предикат:
Call
INGtoString
непосредственно отObject.prototype
возвращения в родную строковое представление данного типа объекта , который"[object Promise]"
в нашем случае. Это гарантирует, что данный объектtoString
метод данного объекта.instanceof
илиisPrototypeOf
.Однако любой конкретный хост-объект , тег которого был изменен с помощью
Symbol.toStringTag
, может вернуться"[object Promise]"
. Это может быть ожидаемый результат или нет в зависимости от проекта (например, если есть пользовательская реализация Promise).Чтобы увидеть, является ли объект родным ES6 Promise , мы можем использовать:
Согласно этому и этому разделу спецификации, строковое представление функции должно быть:
который обрабатывается соответственно выше. FunctionBody находится
[native code]
во всех основных браузерах.MDN:
Function.prototype.toString
Это работает в разных контекстах среды.
источник
Не ответ на полный вопрос, но я думаю, что стоит упомянуть, что в Node.js 10
isPromise
была добавлена новая вызываемая функция, которая проверяет, является ли объект собственным Promise или нет:источник
Вот как пакет graphql-js обнаруживает обещания:
value
Возвращаемое значение вашей функции. Я использую этот код в своем проекте, и у меня пока нет проблем.источник
Вот кодовая форма https://github.com/ssnau/xkit/blob/master/util/is-promise.js
если объект с
then
методом, он должен рассматриваться какPromise
.источник
Если вы используете Typescript , я хотел бы добавить, что вы можете использовать функцию «предиката типа». Просто следует обернуть логическую проверку в функцию, которая возвращает,
x is Promise<any>
и вам не нужно будет делать типы типов. Ниже на моем примереc
приведено либо обещание, либо один из моих типов, которые я хочу преобразовать в обещание путем вызоваc.fetch()
метода.Дополнительная информация: https://www.typescriptlang.org/docs/handbook/advanced-types.html.
источник
Если вы используете асинхронный метод, вы можете сделать это и избежать двусмысленности.
Если функция возвращает обещание, она будет ожидать и возвращаться с разрешенным значением. Если функция возвращает значение, оно будет считаться разрешенным.
Если функция не возвращает обещание сегодня, но завтра возвращает одно или объявляется как асинхронное, вы будете ориентированы на будущее.
источник
Promise.resolve()
источник
Я использую эту функцию как универсальное решение:
источник
после поиска надежного способа обнаружения асинхронных функций или даже обещаний , я использовал следующий тест:
источник
Promise
создадите подкласс и создадите его экземпляры, этот тест может не пройти. это должно работать для большей части того, что вы пытаетесь проверить, хотя.fn.constructor.name === 'AsyncFunction'
это неправильно - это означает, что что-то является асинхронной функцией, а не обещанием - также не гарантируется, что люди могут выполнять обещания подклассаES6:
источник
toString
метод, может просто вернуть строку, которая включает"Promise"
.'NotAPromise'.toString().includes('Promise') === true