Всем привет!
Я использую bluebird в качестве замены глобального объекта Promise. Я пытался обновить typedefs до последней версии, опубликованной @lhecker , и столкнулся с проблемой: global Promsie
теперь не перегружается по умолчанию.
Как мне добиться прежнего поведения? Может быть, например, у нас есть bluebird-global.d.ts?
Ну, ИМХО, предыдущие определения Bluebird тоже не были хорошим решением, потому что они сильно просачивались в глобальное пространство имен, и я думаю, что было хорошей идеей уменьшить избыточные усилия.
Однако предыдущие определения работали, определяя declare var Promise: PromiseConstructor;
, тогда как PromiseConstructor
был предыдущим (глобально определенным) интерфейсом Bluebird.
Т.е. если вы создадите локальный файл *.d.ts
и добавите что-то вроде этого, возможно, это сработает?
import Bluebird = require("bluebird");
declare var Promise: Bluebird<any>;
может сработает может?
к сожалению нет. Потому что у меня много кода, который написан так:
declare function doLoadData(): Promise<Data>
как видите, функция возвращает Promise<T>
, что является стандартным Promsie
, а не bluebird. объявив var Promise: Bluebird<any>
, я перегружу стандартный конструктор Promise, а не интерфейс.
По этой причине я вернулся к типизации 2.0.
@Strate Ах, черт возьми, я написал длинный комментарий о том, что я думаю об этом и что мы должны попытаться сделать. Но, кажется, я забыл отправить его, и он был окончательно потерян...
Кажется, у многих людей есть эта проблема.
Я создал репо, в котором показана проблема: https://github.com/d-ph/typescript-bluebird-as-global-promise .
git clone && npm install
&& npm run tsc
Проблема:
Сторонние файлы d.ts
набираются вместо Promise
. Этот Promise
определяется либо lib.es6.d.ts
машинописного текста (когда "target": "es6"
), либо другими библиотеками, например, core-js
(очень популярен при компиляции в es5 с помощью машинописного текста) . Последний bluebird.d.ts
не объявлен как глобальный Promise
, хотя bluebird.js выставляет себя как глобальный Promise
.
Результат:
Разработчики не могут использовать функциональность bluebird в обещаниях, возвращаемых из стороннего кода (сбой компиляции).
Ожидаемый результат:
Разработчики могут использовать функциональность bluebird для промисов, возвращенных из стороннего кода (компиляция прошла успешно).
Я не уверен, кто поддерживает типизацию bluebird. @lhecker , ты неудачник, которого вернули git blame
. Не могли бы вы рассказать нам, почему предпочтительнее использовать типизацию bluebird таким образом, чтобы проект github, на который я ссылался выше, компилировался?
Текущие обходы:
Promise
используйте это: Promise["config"]({}); Promise.resolve("foo")["finally"](() => { console.log("lol"); })
для отключения компилятора. Т.е. использовать оператор доступа к массиву: [""]
import * as Bluebird from 'bluebird';
declare global { export interface Promise<T> extends Bluebird<T> {} }
Для статических функций Bluebird используйте Bluebird.config({})
вместо Promise.config({})
.
Недостатком является то, что IDE не могут правильно интерпретировать этот хак.
Хм... Для меня было непредвиденным, что у вас, ребята, возникнут проблемы с этим, поскольку вы все используете Bluebird так, как не использую я и многие другие. На самом деле я даже не писал наборы сам! Я просто скопировал отсюда единственные существующие для 3.0, потому что иметь какие-либо типизации для 3.0 лучше, чем не иметь ничего, верно?
Дело в том, что текущее направление, в котором движется TypeScript, явно modules > globals
, что я действительно одобряю. Но это также означает, что модули никогда не должны изменять глобальные объекты, особенно если учесть, что Bluebird
не заменяет глобальный Promise
в каждом случае! Или, говоря так:
Что произойдет с вашей «безопасностью типов», если вы вызовете Promise.noConflict()
буквально в любом месте вашего кода? Он вернет глобальный тип Promise
обратно к исходному и приведет к сбою кода, даже если tsc
сказал, что все в порядке.
Так что да... @d-ph. Второй обходной путь — это то, что вы должны были делать все это время, поскольку он соответствует духу модульных систем. Но я знаю, что это идеальное решение только для библиотек, тогда как для приложений оно _может_ сильно раздражать. Я согласен с тем, что прикладные системы должны, по крайней мере, иметь возможность заменить глобальный объект Promise
, а также иметь соответствующие типизации для этого варианта использования, как это было доступно в версии 2.0.
В конце концов, я думаю, что в свете идеологии TypeScript расширение глобального типа Promise
должно быть сделано _очень_ осторожно (вспомните проблему с noConflict()
и т. д.) и если так, то только как подписка.
IMO путь вперед состоит в том, чтобы написать bluebird-global.d.ts
(или аналогичный) файл некоторого вида, который расширяет глобальный объект Promise
с теми же объявлениями интерфейса, что и в файле bluebird.d.ts
. И если вам нужно их использовать, вы должны явно импортировать их вместо того, чтобы всегда включать их. Таким образом, вы можете иметь безопасные _и_ правильные типизации для большинства случаев использования и особенно при написании библиотек, имея при этом доступ к дополнительным преимуществам перезаписи глобальных Promise
в приложениях.
Если вы считаете, что эта идея хороша, и если у вас осталось свободное время, было бы здорово, если бы вы могли создать PR. Я уверен, что многие были бы очень рады такому вкладу. 🙂
Я формулирую это таким образом, потому что в настоящее время я не в состоянии писать эти типизации из-за нехватки времени и не нуждаюсь в таких типизациях в данный момент. Надеюсь, вы это понимаете.
@lhecker Я думаю, что могу с тобой согласиться. Потому что, если у нас будет глобальное переопределение Promise на bluebird, мы взломаем только компилятор машинописного текста, но не реальный мир. Например, с переопределенным Promise
typescript будет думать, что fetch
возвращает значение bluebird:
import `whatwg-fetch`;
let result = fetch("anyurl"); // this is NOT bluebird promise, but typescript think that it is.
Без обертывания fetch
в Promise.resolve
вы не получите, например, метод .finally
на result
:
import `whatwg-fetch`;
fetch("anyurl").then().finally() // goes to runtime error with overriden global promise, but should be compile error.
Итак, я думаю, что явное импортирование bluebird
при каждом использовании является лучшим решением:
import Promise from "bluebird";
import `whatwg-fetch`;
Promise.resolve(fetch("anyurl")).then().catch() // no compile error, no runtime error
Я собираюсь реорганизовать свой код.
Спасибо за ваш ответ, @lhecker .
Я согласен со всем, что ты сказал. И мне нравится решение @Strate по обертыванию стороннего кода с помощью метода Promise.resolve()
, чтобы преобразовать обещание es6 в Bluebird (или Bluebird в Bluebird, потому что я хочу, чтобы обещание Bluebird было глобальным во время выполнения, поэтому я не не нужно полагаться на сторонний код для правильной обработки своих ошибок, но это не относится к делу).
Похоже, я не знал, как это сделать правильно. Я думаю, что другим было бы полезно больше документации о том, как справиться с этой проблемой, потому что каждый, кто приходит из мира браузерного программирования (в отличие от: из nodejs/typescript), сталкивается с этим после того, как они:
npm install <absolutely everything that uses es6 promise>
npm install bluebird @types/bluebird
Мне также было бы полезно иметь этот раздел «Как использовать в случае, если сторонний код набирается против обещания es6» где-нибудь задокументирован для дальнейшего использования. т.е. имея
import * as Bluebird from 'bluebird';
declare global { export interface Promise<T> extends Bluebird<T> {} }
и
import * as Promise from 'bluebird';
import { Observable } from "rxjs";
let observable = Promise.resolve(new Observable<number>().toPromise());
в файле readme или в блоке документов в верхней части файла bluebird.d.ts.
Что вы думаете?
Я завершил переход от глобального переопределения Promise
к bluebird и обнаружил некоторые проблемы, когда сторонние библиотеки возвращают обещание ES6, которое считалось обещанием bluebird. Таким образом, этот шаг также очистил мою кодовую базу. Я бы порекомендовал всем отказаться от глобальной перегрузки Promise
. Ваше здоровье :)
Я понимаю modules > globals
, но давайте скажем ради аргумента (и/или реальности), что я работаю над большим браузерным SPA, и мне было поручено использовать Bluebird в качестве нашего полифилла Promise.
Я пробую исправление bluebird-global.d.ts
, предложенное @lhecker , с содержимым из @d-ph:
import * as Bluebird from 'bluebird';
declare global { export interface Promise<T> extends Bluebird<T> {} }
Я установил его через typings
, который сгенерировал мой typings/modules/bluebird-global/index.d.ts
:
// Generated by typings
// Source: src/bluebird-global.d.ts
declare module 'bluebird-global' {
// via https://github.com/DefinitelyTyped/DefinitelyTyped/issues/10801
import * as Bluebird from 'bluebird';
global { export interface Promise<T> extends Bluebird<T> {} }
}
Однако, когда я пытаюсь все собрать, TypeScript (v1.8.2) жалуется:
ERROR in /path/to/typings/modules/bluebird-global/index.d.ts
(6,27): error TS2665: Module augmentation cannot introduce new names in the top level scope.
ERROR in /path/to/src/bluebird-global.d.ts
(2,35): error TS2665: Module augmentation cannot introduce new names in the top level scope.
Я взглянул на пример MS для global-modifying-module.ts
https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html
И проблема TS, связанная с этим сообщением об ошибке
https://github.com/Microsoft/TypeScript/issues/6722
Но я в недоумении, что я должен делать. Кто-нибудь может помочь?
Привет.
Не могли бы вы проверить мой репо, который показывает проблему и решение? ссылка . Меня это вполне устраивало, пока я не решил обернуть все промисы от третьих лиц в конструктор промисов bluebird, что я и делаю сейчас. Не могли бы вы подтвердить, что после того, как вы выполните шаги в файле readme, вы не сможете скомпилировать, но после того, как вы раскомментируете
// declare global {
// export interface Promise<T> extends Bluebird<T> {}
// }
он компилируется?
Имейте в виду, что в моем репо используется TS 2 (который теперь стабилен), а вы сказали, что используете 1.8.2. Просто проверьте, что происходит, когда вы обновляете TS до 2.
Наконец, у меня возникли некоторые проблемы с помещением моего решения в мой глобальный файл d.ts
. В итоге я добавил его в каждую точку входа моей компиляции веб-пакета, что решило проблему (теперь это имеет для меня смысл). Я не знаю ваших настроек js, но не могли бы вы попробовать поместить мое исправление в каждый файл, который не работает во время компиляции (или хотя бы в один из них), и проверить, помогает ли это?
Я также хочу иметь возможность «зарегистрировать» реализацию Bluebird Promise
как глобальную Promise
. Прочитал всю ветку, но не понял какой-то части. Часть, предполагающая, что сторонние библиотеки по-прежнему будут возвращать нативную (например, не-Bluebird) реализацию Promise
. Как это могло быть, когда этот сторонний код в какой-то момент вызывает конструктор Promise
( new Promise(...)
), который был заменен реализацией Bluebird на глобальном уровне?
<script src="//.../bluebird.min.js"></script>
<script>
var promise = fetch("some url");
promise.finally(...);
</script>
Разве это не должно работать нормально, поскольку я включил Bluebird, который заменил нативную реализацию Promise
?
Наконец, если я делаю эту полную глобальную замену во время выполнения, я должен иметь возможность сообщить об этом TypeScript, чтобы во время компиляции все промисы также были заменены на Bluebird.
Что мне не хватает?
Если вы используете дистрибутивную версию bluebird (что вы и делаете), то сторонние библиотеки будут использовать Bluebird, поскольку глобальное обещание теперь называется Bluebird. Вы правильно понимаете эту часть. Люди упоминают об обратном, потому что говорят об использовании Bluebird с помощью node.js.
Вся эта ветка посвящена не столь очевидному способу компиляции ts с таким предположением (глобальный Promise — это Bluebird). Если вам удалось это сделать (например, с помощью declare global {}
), то все готово.
@d-ph Но мой вывод заключается в том, что я должен делать это в каждом файле *.ts
, а не только один раз - это правильно? Может быть, краткое изложение окончательного «решения» этой проблемы было бы хорошо. :)
Да, видите ли, здесь нет простого just copy&paste this line to your X file and everyone and their dog are happy now
решения. Или, по крайней мере, я не в курсе.
Последний раз, когда я проверял вас:
import * as Bluebird from 'bluebird'; declare global { export interface Promise<T> extends Bluebird<T> {} }
в каждую точку входа в файл *.ts илиОпять же, решение 1. требует размещения этого кода только в файлах точек входа, а не в каждом файле. По крайней мере, у меня это сработало (webpack + awesome-typescript-loader).
Если вы найдете другое решение, которое буквально требует от разработчиков помещать только 1 строку в 1 файл, пожалуйста, поделитесь с сообществом ;p
Спасибо @d-ph - можете ли вы подтвердить, что вы подразумеваете под «каждой точкой входа *.ts файл»?
Ага. Файл "точка входа" .ts - это файл .ts, который вы загружаете в свой html с помощью тега <script>
. Другими словами, это файл, с которого начинается компиляция.
Только сейчас, после быстрого поиска в Google, я нашел это (не утруждайте себя чтением). Суть в том, что если вы вручную добавите global { export interface Promise<T> extends Bluebird<T> {} }
в bluebird.d.ts, то вам не нужно упоминать об этом снова где-либо еще. У меня нет времени тестировать его прямо сейчас, но я протестировал его с помощью тестового репозитория github, который я создал, и, похоже, он работает.
Другими словами, скачайте bluebird.d.ts и измените это:
// Generated by typings
// Source: bluebird.d.ts
declare module 'bluebird' {
// Type definitions for Bluebird v3.x.x
// Project: http://bluebirdjs.com
class Bluebird<R> implements Bluebird.Thenable<R>, Bluebird.Inspection<R> {
к этому:
// Generated by typings
// Source: bluebird.d.ts
declare module 'bluebird' {
// Type definitions for Bluebird v3.x.x
// Project: http://bluebirdjs.com
global { export interface Promise<T> extends Bluebird<T> {} }
class Bluebird<R> implements Bluebird.Thenable<R>, Bluebird.Inspection<R> {
Очевидно, это не идеально, особенно если вы используете @types/bluebird
(которую вам нужно удалить сейчас, потому что вы будете использовать свой собственный взломанный bluebird.d.ts), но что ж...
Итак, у меня уже есть файл _stubs.d.ts
, к которому я добавил следующее, и я намного ближе. Больше нет жалоб на то, что finally
не существует на Promise
, но по какой-то причине я все еще получаю сообщения об ошибках о том, что delay
не существует на Promise
. Мне не пришлось редактировать bluebird.d.ts
. Будет исследовать, но это может быть отличным решением!
declare module "bluebird-global" {
import * as Bluebird from "bluebird";
global { export interface Promise<T> extends Bluebird<T> { } }
}
Редактировать: моя проблема с delay
заключалась в том, что я вызывал ее статически, например, Promise.delay(2000)
.
С решением, которое я опубликовал выше, я получаю эту ошибку, когда моя функция возвращает Promise<T>
:
ошибка TS2322: введите «Bluebird
' нельзя присвоить типу 'Promise '.
Я предполагаю, что это потому, что теперь, когда я заменил Promise
на Bluebird
, всякий раз, когда я использую then
и т. д., возвращаемое значение вместо этого равно Bluebird<T>
из Promise<T>
.
Вот моя окончательная версия этого хака. Мне не нравится делать это, но мне это нравится больше, чем другие варианты. По сути, мне приходится повторять то, что я использую в интерфейсе, меняя тип возвращаемого значения на Promise
вместо Bluebird
. Это прямая копия и вставка из файла определения Bluebird, отличного от этого.
_stubs.d.ts
declare module "bluebird-global" {
import * as Bluebird from "bluebird";
global {
export interface Promise<T> extends Bluebird<T> {
then<U1, U2>(onFulfill: (value: T) => U1 | Bluebird.Thenable<U1>, onReject: (error: any) => U2 | Bluebird.Thenable<U2>): Promise<U1 | U2>;
then<U>(onFulfill: (value: T) => U | Bluebird.Thenable<U>, onReject: (error: any) => U | Bluebird.Thenable<U>): Promise<U>;
then<U>(onFulfill: (value: T) => U | Bluebird.Thenable<U>): Promise<U>;
then(): Promise<T>;
finally<U>(handler: () => U | Bluebird.Thenable<U>): Promise<T>;
}
}
}
Было бы здорово иметь официальное определение bluebird-global
или bluebird-override
, которое очень похоже на существующее определение, но везде использует Promise
вместо Bluebird
.
Рад, что вы смогли найти решение.
Просто для полноты: как сказал @ProTip , используя bluebird-2.0.d.ts
JustWorksTM. Просто установите его с npm install @types/[email protected]
и добавьте в compilerOptions.types
tsconfig.json:
{
"compilerOptions": {
// (...)
"types": [
"bluebird"
]
},
"include": [
"src/**/*.ts"
]
}
Любые различия между этим .d.ts и текущей версией Bluebird (т.е. 3.x) я рекомендую взломать вручную.
Под JustWorksTM я подразумеваю: он работает для:
а) цель es5
б) цель es6
c) цель es5 с библиотекой core-js
независимо от того, использует ли кто-то настройку сборки (webpack + awesome-typescript-loader) или нет. Кроме того, PhpStorm IDE совершенно не путается.
Сегодня я потратил некоторое время на изучение этой проблемы и фактически создал/обновил эти два билета в Microsoft/TypeScript: https://github.com/Microsoft/TypeScript/issues/10178 и https://github.com/Microsoft/TypeScript . bluebird-global.d.ts
. Чтобы избежать дублирования кода, я обнаружил, что это сработает:
// bluebird-global.d.ts
import * as Bluebird from "bluebird";
export as namespace Promise;
export = Bluebird;
при условии, что два вышеупомянутых тикета решены или найдены обходные пути. А пока я рекомендую использовать bluebird-2.0.d.ts при написании кода для браузера.
@ДжошМакКаллоу
Как это могло быть, когда этот сторонний код в какой-то момент вызывает конструктор Promise (new Promise(...)), который был заменен реализацией Bliebird на глобальном уровне?
Очень легко. Попробуйте в консоли браузера (предпочтительно Chrome):
var NativePromise = Promise;
window.Promise = function() {}; // try to overload NativePromise
NativePromise === Promise; // false. Seems it is overloaded!
// And now, let check with some native code, which return Promise, for example fetch
Object.getPrototypeOf(fetch("")) === Promise.prototype; // false, Whoops!
Object.getPrototypeOf(fetch("")) === NativePromise.prototype; // true! Double whoops!
Привет. Я делал это, и это довольно гладко. Прежде всего, не полагайтесь на bluebird для библиотечных проектов. Для проекта приложения импортируйте bluebird, но не типизацию. В точке входа вашего приложения сделайте следующее:
global['Promise'] = требуется('синяя птица')
Это заменит глобальный объект обещания для приложения и всех включенных библиотек.
Привет, ребята,
@types/bluebird-global
теперь доступен. Эти типизации используют @types/bluebird@^3.0
под капотом и позволяют вам использовать методы bluebird в глобальном Promise
(т.е. компиляция ts не завершается ошибкой).
Пожалуйста, прочтите это , чтобы узнать, как его использовать.
Круто, спасибо @d-ph!
@d-ph Спасибо за @types/bluebird-global
. Нужно ли мне выполнять какой-либо импорт и переназначение в моем проекте, чтобы использовать bluebird в качестве замены глобального обещания?
npm install --save-dev @types/bluebird-global
, а затем следуйте инструкциям, которые я включил в наборы: ссылка (ссылка обновлена 2017-04-02). Это само по себе должно помочь (т.е. не требуется никакого ручного импорта/переназначения).
В качестве примечания: вам больше не нужно упоминать @types/bluebird
в вашем package.json::devDependencies
, потому что это подразумевается автоматически.
Обновление по ссылке в предыдущем комментарии: ссылка
@MichaelTontchev @d-ph есть ли шанс, что мы сможем добавить интерфейс Promise.Inspection
в bluebird-global ?
Привет @ksnyde.
Пожалуйста, поговори со мной. Я сопровождающий.
Не могли бы вы подтвердить, что вы имеете в виду этот Promise.Inspection ?
Все методы из этого интерфейса доступны через bluebird-global
. Т.е. будет скомпилировано следующее:
let promiseInspectionTest = new Promise<void>((resolve) => {});
promiseInspectionTest.value();
Так что мне кажется, что вы просите, чтобы это было выставлено как Promise.Inspection
from bluebird-global
.
Не могли бы вы сказать мне, является ли это серьезной неудачей для вас, если вместо этого вы используете следующее:
import * as Bluebird from "bluebird";
class Foo<T> implements Bluebird.Inspection<T> {
}
Поскольку вы используете bluebird-global
, вы также можете импортировать исходные типы bluebird
без каких-либо явных devDependencies
.
Я бы предпочел не расширять глобальный Promise
дальше без веских причин, потому что заставить эти типизации работать со стандартными типами Promise из lib.d.ts
— тонкое искусство. Я действительно рекомендую обращаться к этому интерфейсу напрямую из типизаций bluebird
, потому что однажды гуру JavaScript могут добавить к стандарту Promise.Inspection
, что нарушило бы типизацию bluebird-global
, что, в свою очередь, создавать ненужные проблемы для конечных пользователей.
Кроме того, даже если бы я добавил интерфейс, вам пришлось бы ждать неопределенное количество времени, пока он будет объединен с master
, потому что в эти дни сопровождающие DT заняты PR.
Ваше здоровье.
Я действительно использовал подход, который вы обсуждали в настоящее время, и это адекватный обходной путь. Или, может быть, "обойти" - неправильная номенклатура.
Мое понимание/восприятие заключалось в том, что идея bluebird-global заключалась в том, чтобы раскрыть надмножество функций Promise, которые предоставляет bluebird, и в этом случае, как пользователь, я _буду_ ожидать, что Bluebird.Inspection
будет выставлен на Promise.Inspection
. Однако, если ваше намерение состоит в том, чтобы просто убедиться, что официальная поверхность API Promises использует Bluebird, то я думаю, что этот «обходной путь» на самом деле является подходящим долгосрочным решением.
Хотя я предпочитаю свою интерпретацию, я буду в порядке, используя представленное здесь решение, если это необходимо.
Хотя я, конечно, понимаю, откуда вы исходите со своими ожиданиями, основная причина, по которой я создал bluebird-global
, заключалась в том, чтобы сообщить TypeScript, что промисы, созданные и возвращенные из стороннего кода, на самом деле являются экземплярами промисов Bluebird, к которым не было другой _не раздражающей_ альтернативы, кроме как выставить все экземпляры Bluebird и статические методы на глобальном символе Promise. Как я упоминал ранее, это делается не просто class Promise<T> extends Bluebird<T> {}
(хотя изначально так и было), а скорее тщательно исправленным глобальным символом Promise. И, как я уже упоминал, я предпочел бы избежать необходимости поддерживать что-либо, для чего есть известная альтернатива, которая буквально не заставляет вас рвать на себе волосы.
Извините, что сказал "нет" этому. Если будет больше людей, запрашивающих эту конкретную функцию, я пересмотрю ее добавление. Я не хочу звучать здесь авторитетно — это проект с открытым исходным кодом, и любой, вероятно, мог бы внедрить эту функцию. не превышает затрат на его содержание.
Ваше здоровье.
имеет смысл. спасибо за размышления о вашем подходе.
@d-ph имеет ли для вас какое-либо значение следующее... Я получаю сообщение об ошибке, в котором говорится, что "отражать" не является функцией в приведенном ниже коде:
И хотя в Intelisense редактора правильно определяется, что свойство p
в функции сопоставления является обещанием Bluebird и имеет расширенную поверхность API, которую можно найти только в Bluebird (по сравнению с Promise).
Я просто не могу сделать из этого головы или решки. Я заметил, что когда я проверяю _type_ переменной итератора карты, она отображается как:
Я предположил, что именно поэтому я получаю ограниченную поверхность API, определенную bluebird-global
, но я не знаю, почему она не разрешается правильно.
Привет.
Я не могу воспроизвести :/ Эти фрагменты кода работают в моей настройке.
Прежде всего. вы не используете bluebird-global
в своей функции allSettled()
. Вы печатаете напрямую, используя Bluebird
. Это не проблема, но, возможно, вы не знали об этом. В следующем фрагменте используется bluebird-global
:
function allSettled<T>(promises: Array<Promise<T>>) {
const reflections = Promise.all<T>(promises.map((promise => {
return promise.reflect();
})));
return reflections;
}
Т.е. этот сниппет вводит в глобальный Promise
(который пропатчен методами Bluebird в bluebird-global.d.ts
). Как я уже сказал: это для вашего сведения, если вы не знали об этом, потому что и ваш, и мой фрагменты работают одинаково.
Вернемся к проблеме отсутствия методов Bluebird. Мое предположение таково: вы не заменяете глобальный Promise
на Bluebird во время выполнения, а затем запускаете allSettled()
с промисами, созданными с использованием глобального промиса вместо Bluebird.
Позвольте мне показать вам мой код и несколько скриншотов.
function allSettled<T>(promises: Array<Promise<T>>) {
const reflections = Promise.all<T>(promises.map((promise => {
return promise.reflect();
})));
return reflections;
}
let promises = [
Promise.resolve(),
Promise.reject(new Error("rejected test")),
new Promise<void>(() => {}),
];
let reflections = allSettled(promises);
console.log(reflections);
// this is part of my entry point
/*
* Promise
*/
import * as Promise from 'bluebird';
import 'expose-loader?Promise!bluebird';
_Pic 1: Обещание в Array.map() — это Bluebird (вы можете сказать по наличию свойств подчеркивания, таких как: _bitField
)_
_Рис. 2: Promise.reflect действительно определен в цикле_
Не могли бы вы установить точку останова js, как я, в Chrome Dev Tools и посмотреть, что такое promise
внутри .map
? Еще лучше: не могли бы вы просто ввести Promise
в консоли и посмотреть, получите ли вы следующий вывод:
Если вы получите следующий вывод, значит, вы НЕ заменили глобальный Promise на Bluebird во время выполнения:
В таком случае вам нужно это сделать. Например, как я в вашем входном файле.
Наконец, небольшой отзыв разработчиков: лично я бы использовал Promise<T>[]
вместо Array<Promise<T>>
. Но это, конечно, на усмотрение разработчиков. Я только что пришел из C, где нет шаблонов и где разработчики используют оператор доступа к массиву для определения типов.
Ваше здоровье.
Большое спасибо за такое подробное объяснение. Одна вещь, в которой я не был уверен, заключалась в том, на что указывает следующее:
import 'expose-loader?Promise!bluebird';
@ d-ph аааа, мне не хватало одной строчки выше. Без вашей помощи никогда бы туда не попал! Это может быть только я, но я думаю, что было бы полезно, если бы в тексте README
была ссылка на использование Expose Loader .
Хотя, честно говоря, я все еще не на 100%, если для этого потребуется использование веб-пакета? Моя цель не браузер, а просто функция узла.
На самом деле, похоже, что он работает не полностью, поскольку я получаю следующую ошибку при попытке выполнить файл (в редакторе до запуска нет ошибок):
Ошибка: не удается найти модуль «expose-loader? Promise! bluebird»
Вы правы, expose-loader
— это веб-пакет (загрузчик веб-пакетов). Поэтому, если вы не позволите веб-пакету обработать этот оператор import
, он не запустится (т.е. вы получите ошибку «Не удается найти модуль»).
Если вы не можете/не используете webpack, вам нужно найти другой способ сделать глобальное обещание Bluebird во время выполнения. У меня нет большого опыта работы с узлом, но я нашел это только сейчас: https://github.com/petkaantonov/bluebird/issues/1026 (переопределение Promise
в узле).
Я рекомендую узнать, как использовать async/await в последнем узле 7. Вам повезло жить во времена, когда это доступно для разработчиков.
Что касается упоминания об использовании expose-loader
в README: я уже указывал в начале файла d.ts
, что это работа разработчиков по замене Promise на Bluebird во время выполнения: ссылка . Так как существует так много способов сделать это, я на самом деле не упомянул ни один из них. Имейте в виду, что expose-loader
— это просто эквивалент запуска window.Promise = Bluebird
. Что ж, надеюсь, Google проиндексирует мой ответ, так что люди не будут больше искать возможные варианты слишком долго ;p
@d-ph с нетерпением жду встроенного async-await, но все эти функции есть в AWS Lambda, поэтому я пока привязан к узлу 6.10.x. В будущем проходе этих функций я, вероятно, все равно переключусь на async-await и заставлю Typescript транспилировать его в ES2015, но пока не хочу вводить это.
В любом случае, спасибо за ссылку @d-ph, я попробую этот подход. Да, и, если кому-то интересно, окончательная версия этих функций (которые очень удобны в обещанной стране):
export function allSettled<T>(promises: Array<Promise<T>>) {
const reflections = Promise.all<Promise.Inspection<T>>( promises.map((p => p.reflect())) );
return reflections as Promise<Array<Promise.Inspection<T>>>;
}
export function settleProps<T>(promiseHash: IDictionary<Promise<T>>) {
const reflections: IDictionary<Promise<Promise.Inspection<T>>> = Object.keys(promiseHash)
.reduce((newObject: IDictionary<any>, key: string) => {
newObject[key] = promiseHash[key].reflect();
return newObject;
}, {} as IDictionary<Promise<Promise.Inspection<T>>>);
return Promise.props(reflections) as Promise<IDictionary<Promise.Inspection<T>>>;
}
Доступна полная информация об интеллисинхронизации/типе, что очень приятно.
@ d-ph, надеюсь, все в порядке, если я оживлю это с соответствующим вопросом ...
Когда я пытаюсь использовать bluebird-global, я, кажется, получаю немного различий в определениях как в then
, так и catch
. Я вижу, что они обрабатываются специально в bluebird-global, но, похоже, это ограничивает меня стандартным API-интерфейсом Promise вместо получения bluebird. Например:
Promise.resolve(true).catch(Error, () => false)
терпит неудачу, поскольку catch
ожидает только 1 аргумент.
И еще вопрос:
Promise.resolve([3]).map((n: number) => true)
Не удается с:
│TS2345: Argument of type '(n: number) => boolean' is not assignable to parameter of type 'IterateFunction<{}, boolean>'. │
│ Types of parameters 'n' and 'item' are incompatible. │
│ Type '{}' is not assignable to type 'number'. │
Они должны работать или я что-то не так делаю? Они работают во время выполнения, они просто не проверяют тип.
Спасибо!
Привет,
Что касается .catch(Error, function)
, вы правы, говоря, что .then
, .catch
(и другие) обрабатываются иначе, чем остальные функции Bluebird. Однако конкретное переопределение .catch(Error, function)
включено в bluebird-global . Я дважды проверил, и я могу скомпилировать:
Promise.resolve(true).catch(Error, () => false)
Я проверял с TS 3.0.1 и 2.9.2. Я не знаю, почему вы можете столкнуться с проблемой здесь. Возможно, в вашем проекте TS есть что-то конкретное, что переопределяет глобальное обещание после bluebird-global
. Я не знаю. Возможно, попробуйте сузить круг причин проблемы, начав с очень простого проекта TS, а затем добавив больше зависимостей из вашего текущего проекта, и посмотрите, в какой момент он сломается.
О другой проблеме: я не знаю, почему это не работает. И да, это должно работать. Пожалуйста, создайте для этого проблему на github, и мы пойдем оттуда. Вот несколько фактов о проблеме:
bluebird-global
делает с .map()
, просто повторно использует определение типа bluebird.d.ts
's .map()
. Другими словами, дефект не должен быть связан с типизацией bluebird-global
.Promise.map()
, но работает на Bluebird.map()
:import Bluebird = require('bluebird');
Bluebird.resolve([3]).map((n: number) => true); // works
Promise.resolve([3]).map((n: number) => true); // fails
n
должен быть {}
), я пришел к выводу, что bluebird.d.ts
также не должен работать, но работает по неизвестной мне причине. Короче говоря, после удаления всех слоев абстракций набирается .map()
:map<U>(
mapper: (
item: U,
index: number,
arrayLength: number
) => U | PromiseLike<U>,
options?: Bluebird.ConcurrencyOption
): Bluebird<T extends Iterable<any> ? U[] : never>;
В нем говорится, что возвращаемый тип функции mapper
должен быть таким же (или обещанием того же самого), что и тип item
. В вашем примере тип элемента — number
, а возвращаемый тип — boolean
. Я не могу понять, почему это компилируется при прямом использовании Bluebird
, но не при использовании глобального промиса. Кстати, это все еще не работает, когда я меняю тип возвращаемого значения в вашем примере на любое число. Однако это работает, когда я говорю, что item
может иметь тип any
. Что-то не так с bluebird.d.ts
's type IterableItem<R>
и его использованием в этом контексте.
@d-ph
Редактировать:
Я снова проверил «неслоистую» форму $# map()
28$#$, и функция mapper
не типизирована, чтобы иметь тот же тип возвращаемого значения, что и тип item
(я думал, что это был). Виноват.
Все, что bluebird-global делает с .map(), просто повторно использует определение типа bluebird.d.ts .map().
Является ли проблема в том, что когда вы копируете тип из универсального класса Bluebird<R>
, он по умолчанию принимает значение {}
для R
, поскольку он не может вывести его из родителя?
Интересно, сработает ли map: typeof Bluebird<T>.prototype.map
? (это я еще не пробовал)
ВАЖНЫЙ:
Если вы используете @types/bluebird-global
, удалите из своих зависимостей @types/bluebird
, как сказал @d-ph
npm install --save-dev @types/bluebird-global
, а затем следуйте инструкциям, которые я включил в наборы: ссылка (ссылка обновлена 2017-04-02). Это само по себе должно помочь (т.е. не требуется никакого ручного импорта/переназначения).В качестве примечания: вам больше не нужно упоминать
@types/bluebird
в вашемpackage.json::devDependencies
, потому что это подразумевается автоматически.
Наличие обоих вызывало несоответствие между типами, возвращаемыми @types/bluebird
, и моим глобальным обещанием ( @types/bluebird-global
)
Самый полезный комментарий
Привет, ребята,
@types/bluebird-global
теперь доступен. Эти типизации используют@types/bluebird@^3.0
под капотом и позволяют вам использовать методы bluebird в глобальномPromise
(т.е. компиляция ts не завершается ошибкой).Пожалуйста, прочтите это , чтобы узнать, как его использовать.