Knex: как настроить использование стандартного обещания ES6?

Созданный на 22 июл. 2016  ·  80Комментарии  ·  Источник: knex/knex

при работе с узлом v6 есть ли способ настроить использование только стандартного обещания ES6 (избавиться от зависимости bluebird)?

например, в пакете promise-queue по умолчанию используется любой глобально доступный Promise,

https://www.npmjs.com/package/promise-очередь

или пользователь может явно настроить его с помощью Queue.configure(require('vow').Promise);

Так может ли этот пакет реализовать подобную стратегию?

discussion

Самый полезный комментарий

:+1: для нативных промисов.

Все 80 Комментарий

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

@johanneslumpe Это фактор в некоторых приложениях, с knex Я очень сомневаюсь, что используемая библиотека Promise оказывает какое-либо существенное влияние на производительность. Обсуждалось, что мы должны написать весь код Promise , чтобы использовать только API A+, чтобы bluebird не понадобились.

После этого должно быть легко переопределить, какая библиотека Promise используется.

Я согласен, что это кажется бессмысленным в такой библиотеке, как knex. Учитывая, что кодовая база knex уже импортирует свой собственный файл promise.js внутри, технически его все равно будет очень легко реализовать, при условии, что API тот же _ (в чем я не уверен?)_. В этом файле можно по умолчанию использовать глобальные промисы, иначе потребуется настроенная библиотека или что-то в этом роде.

все дело в том, чтобы предоставить пользователям выбор; для пользователей Node V6+ предоставьте как можно меньше возможностей для управления зависимостями от сторонних разработчиков.

Любопытно: вы хотите заменить более быструю библиотеку более медленной встроенной?

то, что вы говорите, может быть правдой в прошлом, но как насчет сейчас или через 6 месяцев? (Я провел некоторый поиск и могу найти только некоторые тесты 2015 года, если у вас есть более свежее сравнение, опубликуйте несколько ссылок)
Я считаю, что весь смысл превращения Promise в стандарт ES6 заключается в том, чтобы люди могли легко использовать Promise, не полагаясь на сторонние библиотеки, а основная команда Node или V8 не может навсегда закрывать глаза на разницу в производительности, пока лицензии с открытым исходным кодом двух проектов совместимы, они даже могут заимствовать некоторый код; или просто дайте им немного времени. Я считаю, что встроенный Promise может быть быстрее и лучше.

см. Amazon AWS-SDK: по умолчанию также используется любое глобально доступное обещание; в то же время дайте пользователю возможность настроить любимую библиотеку Promise.

http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-making-requests.html#Support_for_Promises

все дело в выборе

Если подумать, это будет не так просто, как изменение одного файла. Knex в настоящее время во многом полагается на служебные функции Bluebird, такие как .timeout , .tap , .reduce , .map и т. д., которые я предполагаю и ожидаю, что они не существуют. в обещаниях ES6.

Я заинтересован в поддержке обещаний ES6. В идеале нам потребуется второй аргумент конструктора Knex , который принимает конструктор Promise . Обратная совместимость будет достигнута так:

const Promise = require('bluebird');
const knex = Knex(myKnexConfig, Promise);

Возможно, мы могли бы условно использовать псевдонимы map , filter и т. д. на основе их присутствия в предоставленных Promise.prototype ?

Я думаю, что это должно быть довольно низко в списке приоритетов, и это потребует довольно много внутренних изменений, не говоря уже о снижении производительности (хотя, по общему признанию, некоторое время не видел тестов) и тот факт, что люди могут полагаться на том, что возвращаемые промисы — это блюберд (для условных catch и т.д.).

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

для людей, которым нужны минимальные сторонние зависимости, родной Promise может быть лучше,

например, @google-cloud и многие другие библиотеки, вы можете сделать по умолчанию использование собственного промиса и принять параметр промиса для тех, кто хочет использовать сторонние библиотеки

https://googlecloudplatform.github.io/google-cloud-node/#/docs/google-cloud/

var gcloud = require('google-cloud')({
  promise: require('bluebird')
});

@tgriesser Я знаю, что это старая проблема, но теперь у нас есть async / await как в стабильной версии, так и в Carbon.

Учитывая, что для async/await предпочтительнее работать с собственными промисами ( согласно спецификации, функции async должны возвращать нативное обещание ), является ли это более высоким приоритетом?

Если Knex хотел бы иметь встроенную поддержку промисов, но в настоящее время это не рассматривается, будет ли приветствоваться PR?

Спасибо за ваше время.

Функции @malexdev async действительно возвращают собственные промисы, в настоящее время они не имеют большого отношения к knex. Это не означает, что await нуждается в нативных промисах для правильной работы. Не могли бы вы уточнить, в каком случае это проблема/выгода (кроме удаления одной зависимости)?

При этом я не против отказа от Bluebird, но он действительно нуждается во внутренних изменениях. Возможно, нам потребуется реализовать некоторые методы, которые в настоящее время доступны из bluebird для API-интерфейсов knex, если только старые функции не скрыты под каким-либо переключателем конфигурации и по умолчанию будут использоваться собственные промисы. Таким образом, миграция не будет невозможной для людей, которые сообщают о том, что knex возвращает обещания bluebird .

@elhigu Основное преимущество для меня лично заключается в том, что я использую TypeScript с правилом TSLint, обеспечивающим использование собственных промисов для await , поэтому мне приходится заключать все вызовы Knex в Promise.resolve() . Я понимаю, что это не имеет ничего общего с Knex, и, вероятно, проблема уникальна для меня.

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

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

Да, я приземлился здесь из-за проблемы с машинописным текстом - я использую Knex в качестве механизма SQL для моей библиотеки хранилища данных с несколькими хранилищами, и хотя у меня двойственное отношение к обещаниям нативных и bluebird, я не могу легко использовать машинопись на кнекс по этой причине. Я отношусь к knex thenables как к нативной спецификации (я не использую ни одно из расширений bluebird), но машинописный текст выдает мне ошибку при возврате Bluebirdкогда объявление метода является обещанием.

Это своего рода два уровня здесь, так как мы имеем дело как с реализацией промисов, так и с типизацией для knex (которые обрабатываются разными разработчиками), но я в основном застрял здесь - технически я нарушаю контракт типа возвращая Bluebird, когда я объявил Promise (я думаю, в API Promise есть вещи, которые Bluebird не поддерживает?), но мне действительно не хочется помещать кучу return Promise.resolve(bluebirdthing) везде.

Я провел достаточно времени, копаясь в внутренностях кнекса за последний год и работая с промисами в целом, поэтому я был бы готов взять что-то здесь и поработать над PR, чтобы разбить реализацию промиса на модули, если люди захотят — не могли бы вы быть открыты для пиара? В конечном итоге это будет что-то вроде того, что упоминал @elhigu - повторная реализация некоторых служебных функций для использования любого конструктора Promise, который был передан при создании экземпляра, чтобы избежать необходимости перезаписи кода. Насчет производительности, конечно, не уверен, но это то, что можно сравнить.

Было бы здорово, если бы все это было сделано с помощью async / await, и я не спешу исправлять это (в конечном итоге для моего варианта использования я просто помечаю вещи как any и работаю с этими ветки кода, как если бы они были javascript, а не машинописным текстом).

@ericeslinger Я не понимаю, почему реальная реализация Promise, которая используется, может вызвать проблемы с TypeScript, я полтора года смешивал bluebird и нативные обещания без каких-либо проблем ...

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

Какая версия машинописного текста и есть ли какой-либо пример проекта для воспроизведения... например, репозиторий github со сценарием запуска npm.

Это своего рода два уровня здесь, так как мы имеем дело как с реализацией промисов, так и с типизацией для knex (которые обрабатываются разными разработчиками), но я в основном застрял здесь - технически я нарушаю контракт типа возвращая Bluebird, когда я объявил Promise (думаю, в API Promise есть вещи, которые Bluebird не поддерживает?), но мне действительно не хочется помещать кучу return Promise.resolve(bluebirdthing) везде.

Это типизация knex из npm? Являются ли типы bluebird из npm? Какие пакеты? Мне трудно понять, почему это произошло. Если есть отдельный тип Bluebird, он должен быть унаследован от родного обещания и должен быть в порядке для возврата из API, который сообщает, что они вернут обещание. Из описания звучит, что проблема в очень ломаных реализациях типизации. И это не имеет отношения к этой проблеме (typescript не возражает против реальных типов, поэтому он не будет знать, что возвращает knex).

Я получаю данные из репозитория DefinitelyTyped , устанавливая @types/knex . Это определение влечет за собой @types/bluebird , и все методы knex типизированы как возвращающие объекты Bluebird.

Как конкретная вещь, я не могу сделать это:

function mungeData(v: any): DataItem {} 
function foo(): Promise<DataItem[]> {
  return knex('data').select()
  .then((rows) => rows.map(row => mungeData(row)))
} // error, cannot return Bluebird<DataItem[]> as Promise<DataItem[]>

используя эти типизации. Это связано с тем, что knex.select().then() набирается для возврата Bluebird в репозитории DefinitelyTyped, и они соединяются вместе, чтобы создать больше Bluebirds, и говорят что-то вроде return foo as Promise<any>() , когда foo является Bluebirdпотерпит неудачу (по крайней мере, в машинописном сценарии 2.4), потому что Bluebirds не могут быть назначены промисам (в соответствии с этим им не хватает [Symbol.toStringTag], поэтому принуждение одного к другому было бы ошибкой, хотя и небольшой ошибкой).

Вместо этого я могу изменить на

function bar(): Promise<DataItem[]> {
  return Promise.resolve<DataItem[]>(foo())
}

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

До вчерашнего дня я вообще не использовал @types/knex — я просто печатал knex как any . Код отлично работает в любом случае во время выполнения (по крайней мере, для моего варианта использования), это просто

@elhigu : проблема не в нарушении реализации ввода.
TypeScript устанавливает тип для функций async как Promise<[type]> , что соответствует спецификации JS.
Knex возвращает Bluebird<[type]> , что точно отражает типизация.

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

Это ложь компилятору, так как функции Knex действительно возвращают Bluebird s. Не интересует.
Вы правы в том, что Bluebirds совместимы с Promises, но часть сделки с TypeScript заключается в том, что вы фактически возвращаете то, что, как вы говорите, возвращаете.

При возврате Bluebird из функции, которая была введена для возврата Promise , TypeScript ругается, потому что тип Bluebird не совпадает с типом Promise .
Мы можем использовать различные трюки (например, @ericeslinger упомянул об использовании any или обертывании в Promise.resolve() ), но в конце концов из-за таких трюков мы теряем большую часть что предоставляет TypeScript.

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

Я понимаю, что вы просто пытаетесь помочь, но, честно говоря, вместо того, чтобы слышать «вы могли бы сделать это таким образом», я хотел бы услышать, приемлемы ли изменения обещаний, предложенные мной / @ericeslinger / @c0b , чтобы я мог либо начать на пиар что ли.

@malexdev @ericeslinger Спасибо за дополнительную информацию! Похоже, что на самом деле невозможно наследовать свой собственный класс от Promise , так что это может быть причиной того, что возврат Bluebirds из функции, которая типизирована как Promise<>, не работает :(

@ericeslinger В любом случае, это не проблема, когда вы создаете функции async , поскольку они автоматически оборачивают результаты в собственные промисы внутри. Следующее без проблем соответствует типизации из @types/bluebird и скомпилировано либо в ES2015, либо в ESNEXT.

import * as Bluebird from 'bluebird';

// declaring function async converts bluebird implicitly to native Promise
async function asyncReturningPromise(): Promise<string> {
    const blueBirdPromise = new Bluebird<string>((resolve, reject) => { 
        resolve('yay asyncReturningPromise');    
    });
    return blueBirdPromise;
}

// main func to run the code using async / await
Bluebird.resolve().then(async () => {
    console.log("await function returning promise (bluebird)", await asyncReturningPromise());

    const blueBird = new Bluebird((resolve, reject) => { resolve(); });
    const returnedFromAsync = asyncReturningPromise();

    console.log("Bluebird instanceof Promise:", blueBird instanceof Promise);
    console.log("async retval instanceof Promise:", returnedFromAsync instanceof Promise);
});

выход:

await function returning promise (bluebird) yay asyncReturningPromise
Bluebird instanceof Promise: false
async retval instanceof Promise: true

Итак, на данный момент, когда вы используете API-интерфейсы knex, вам действительно нужно сообщить, что вы возвращаете Bluebird, если вы не используете асинхронные функции/методы, которые автоматически переносят bluebird в собственные промисы.

@malexdev

Это ложь компилятору, так как функции Knex действительно возвращают Bluebirds. Не интересует.
Вы правы в том, что Bluebirds совместимы с Promises, но часть сделки с TypeScript заключается в том, что вы фактически возвращаете то, что, как вы говорите, возвращаете.

На самом деле дело с typescript в том, что достаточно, чтобы возвращаемый объект правильно реализовывал интерфейс, например, это прекрасно:

class FakePromise<T> implements Promise<T>  {
    [Symbol.toStringTag]: "Promise";
    then<TResult1, TResult2>(onfulfilled?: (value: T) => TResult1 | PromiseLike<TResult1> | null | undefined, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2> | null | undefined): Promise<TResult1 | TResult2> {
        return new Promise((resolve, reject) => { resolve('Im totally broken and fake!); });
    }
    catch<TResult>(onrejected?: (reason: any) => TResult | PromiseLike<TResult> | null | undefined): Promise<T | TResult> {
        throw new Error("Method not implemented.");
    }
}

// this works and  fake promise instance has nothing to do with native promise
function returningPromiseInterface(): Promise<string> {
    const fakePromise = new FakePromise<string>();
    return fakePromise;
}

// compiling this fails, because looks like Bluebird actually doesn't implement Promise interface correctly
function asyncReturningPromise(): Promise<string> {
    const blueBirdPromise = new Bluebird<string>((resolve, reject) => { 
        resolve('yay asyncReturningPromise');    
    });
    return blueBirdPromise;
}

Это портит instanceof, но typescript на самом деле даже не обещал, что вы вернете собственный экземпляр Promise, только интерфейс.

При возврате Bluebird из функции, которая была типизирована для возврата Promise, TypeScript ругается, потому что тип Bluebird не совпадает с типом Promise.
Существуют различные трюки, которые мы можем использовать (например, то, что @ericeslinger упомянул об использовании any или обертке в Promise.resolve()), но, в конце концов, подобные трюки заставляют нас терять большую часть того, что предоставляет TypeScript.

Я бы не хотел видеть, как людям приходится делать такие трюки / менять реализации JS только для того, чтобы удовлетворить плохие типизации.

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

Я понимаю, что вы просто пытаетесь помочь, но, честно говоря, вместо того, чтобы слышать «вы могли бы сделать это таким образом», я хотел бы услышать, приемлемы ли изменения обещаний, предложенные мной / @ericeslinger / @c0b , чтобы я мог либо начать на пиар что ли.

Спасибо за понимание :) Изменение knex для использования нативных обещаний уже было начато и реализовано в какой-то момент в прошлом году, затем @tgriesser снова вернул его, поэтому я бы сказал, что пока лучше не начинать это изменение.

Также я по-прежнему рассматриваю эти проблемы машинописного текста, упомянутые в этой теме, как проблемы в объявлении типизации (почему bluebird не реализует Promise правильно... мне нужно покопаться в этом подробнее?), чем проблемы в реализации knex.

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

:+1: для нативных промисов.

@elhigu :

На самом деле дело с машинописным текстом в том, что возвращаемый объект правильно реализует интерфейс

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

Итак, по-прежнему 👍 для нативных промисов (с которыми я все еще готов помочь), но теперь я вижу, что мою насущную проблему можно решить, исправив типизацию Bluebird. Спасибо за информацию.

Итак, я начал довольно часто использовать TypeScript, и мне это нравится, и теперь я понимаю, что проблемы здесь — настоящая боль. По мере того, как async/await завоевывает все больше позиций в недавних Node Land, утилита Bluebird fns ( map , reduce , tap , bind , return ) становятся менее полезными. Я был бы не против продолжать использовать Bluebird для внутреннего использования, но официально «объявил бы устаревшим» общедоступный API построителя запросов knex, возвращающий все дополнительные служебные методы.

С этим слиянием мы могли бы затем обновить определения TypeScript, чтобы отказаться от типизации Bluebird (кроме toBluebird ) и изменить типизацию Bluebird на типизацию Promise.

Если у кого-то есть пропускная способность, и он хочет решить эту проблему, вот что я думаю о плане действий:

  • [ ] Добавлено предупреждение об устаревании для всех проксируемых методов Bluebird ( tap , map , reduce , bind , return ).
  • [ ] Добавьте метод .toBluebird() , который будет путем миграции для тех, кто хочет продолжать использовать Bluebird (они могут сделать это с помощью простого поиска/замены всех вызовов вышеуказанных методов и просто добавить его перед так называются)
  • [ ] Как только это будет объединено/вырезан новый релиз (0.15), мы сможем обновить определения Typescript.
  • [ ] В конечном итоге мы можем полностью отказаться от этих методов. Этот более простой API открывает путь к использованию нативных Promises и async/await, когда это имеет смысл.

Дайте мне знать, если это имеет смысл, и если кто-то заинтересован в этом.

Определенно было бы интересно помочь с этим.

Означает ли это, что Knex начнет поддерживать свои собственные определения TypeScript? Позволит нам делать кое-что интересное с дженериками, которые никогда не будут поддерживаться автогенерируемыми типизациями.

Я начал этот форк как первую попытку добавить поддержку нативных промисов:
https://github.com/tgriesser/knex/pull/2523/files

Нравится этот комментарий от 2016 года:

Любопытно: вы хотите заменить более быструю библиотеку более медленной встроенной

Удивительно, как много может измениться за 2 года.

При возврате Bluebird из функции, которая была типизирована для возврата Promise, TypeScript ругается, потому что тип Bluebird не совпадает с типом Promise.

@malexdev На самом деле машинописный текст использует структурную типизацию (поток использует номинальную типизацию и будет работать так, как вы описываете), поэтому, если ваш тип соответствует интерфейсу Promise , он совместим с Promise , независимо от того, extends / implements это или нет.

Как это продвигается? Я считаю, что хорошим первым шагом было бы выделить вызовы конкретных методов Bluebird в knex (т.е. пока не удалять их). Затем следует удаление bluebird и предоставление опции для пользовательского конструктора Promise (и предоставление людям, использующим методы Bluebird, пути обновления).

Я не начинаю работу над первым шагом, если нет возражений. Существующая работа, похоже, заглохла.

@qubyte Я не думаю, что предпринимаются активные усилия по внесению изменений, здесь и там были внесены дополнительные изменения, но это все.

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

@tgriesser Есть ли какие-нибудь мнения, когда нам следует продолжить работу над этим (если вообще)? Для меня апрель следующего года покажется разумным временем для этого, когда Node 6 LTS достигнет конца очереди.

Интересная информация в 2018 году:

promises-native-async-await имеет лучшую производительность, чем promises-bluebird в узле 10.
Ссылка: https://github.com/petkaantonov/bluebird/tree/master/benchmark

Таким образом, производительность больше не является причиной для сохранения bluebird. Мы должны пойти на async/await.

promises-native-async-await имеет лучшую производительность

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

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

Есть ли какие-либо обновления по этому поводу?

@cfanoulis То же самое остается в силе. Когда наступит апрель, мы сможем отказаться от поддержки Node 6 и начать удаление bluebird.

какое-нибудь обновление 2019 года? /cc некоторым основным участникам или сопровождающим или всем @здесь , кому это небезразлично: @johanneslumpe @tgriesser @wubzz @elhigu из https://github.com/tgriesser/knex/graphs/contributors?type=c&from=2018-01- 01&to=2019-12-31

С другой стороны, сообщество JavaScript — это такой динамичный, яркий и иногда жестокий мир, что каждые 3 или 2 года (а то и быстрее) появляются замены чему-то, что мы знали раньше, подумайте о Grunt, Gulp => Webpack, инструментах, библиотеки, фреймворки полностью конкурируют на всех уровнях. Таким образом, для более старых библиотек, если вы прекращаете вносить инновации или замедляете поддержку новых стандартов (подумайте об итераторах async/await ES2019...), вас в конечном итоге заменят

Я только что провел небольшое исследование, похоже, что на уровне DB ORM также есть много альтернатив, TypeORM может быть хорошим... (я останавливаюсь здесь, чтобы не говорить больше...)
https://bestofjs.org/tags/db
https://bestofjs.org/projects/typeorm

@ c0b не нужно копировать. В любом случае, я получаю письма от всех комментариев. @kibertoad только что сказал все, что нужно было сказать в своем последнем комментарии ... Эта проблема также не имеет ничего общего с тем, что knex лучше поддерживает функции async / await ES2019, а knex не является ORM, поэтому я не уверен, что это был за комментарий на самом деле о.

Если вам нужна хорошая ORM, могу порекомендовать objection.js. Он также реализован поверх knex, вот довольно хорошая ветка об этом https://github.com/Vincit/objection.js/issues/1069 .

В какой-то момент этот кнекс будет заменен, но не каким-либо ORM. Его можно заменить другим построителем запросов, который имеет более чистую кодовую базу и более согласованный API. Как, например, knex 1.0, может быть;)

Кроме того, если knex будет заменен, меня это полностью устроит, для меня меньше работы: D

Я считаю, что есть WIP: https://github.com/tgriesser/knex-next

Просто хотел также упомянуть, что неиспользование собственных промисов приводит к https://github.com/nodejs/node/issues/22360 при использовании async_hooks , что приводит к потере текущего контекста.

Поверьте, нам не нужны дополнительные причины для переезда, мы хотим сделать это так же сильно, как и вы :). Тем не менее, нам все еще нужно выпустить еще пару исправлений для ветки Node 6, а затем (наконец) мы ее отбросим и начнем постепенно отказываться от bluebird.

После слияния #3227 мы, наконец, можем начать!

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

Я думаю: сделать проект, добавить пару задач и посмотреть, может ли кто-нибудь (может быть, у меня есть время) получить назначение и установить какие-то даты?

@chaffeqa Скоро создадим несколько более детальных задач, подготовьте # 3250 для первого раунда простых изменений. В основном нам нужно заменить использование bluebird.tap, bluebird.method и bluebird.try чем-то нативным. Если у вас уже есть время, вы можете попробовать разветвить #3250 и взглянуть на все оставшиеся требования «bluebird» (я бы рекомендовал начать с неспецифических для диалекта, чтобы вы могли быстро проверить работоспособность, запустив test:sqlite без установки Docker).

@qubyte Если вы хотите внести свой вклад, сейчас самое время!

@kibertoad Могу ли я сейчас безопасно использовать async/await?

Вы имеете в виду кодовую базу knex? Конечно. В своем ты всегда был :-D

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

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

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

На самом деле я начал с нескольких POC, и я думаю, что это самый простой из них:

class DisposablePromise extends Promise {

  disposerFunc = null;
  originalResource = null;

  then(onFulfilled, onRejected) {
    const $onFulfilled = this.wrap(onFulfilled);
    return super.then($onFulfilled, onRejected).copyContext(this);
  }

  copyContext(promise) {
    this.disposerFunc = promise.disposerFunc;
    this.originalResource = promise.originalResource;
    return this;
  }

  disposer(disposerFunc) {
    this.disposerFunc = disposerFunc
  }

  isDisposable() {
    return !!this.disposerFunc
  }

  wrap(onFulfilled: any) {
    const $onFulfilled = (result: any) => {
      if (this.disposerFunc && !this.originalResource) {
        this.originalResource = result
      }
      if (result instanceof Promise) {
        return onFulfilled(result);
      } else {
        const res = onFulfilled(result)
        if (this.disposerFunc) {
          this.disposerFunc(this.originalResource)
        }
        return res
      }
    };

    return $onFulfilled;
  }
}

И другой:

      var DisposablePromise = function DisposablePromise() {
          var self = DisposablePromise.convert(Promise.resolve());
          return self;
      };
      DisposablePromise.convert = function convert(promise, props) {
          promise.__proto__ = DisposablePromise.prototype;
          return props ? Object.assign(promise, props) : promise;
      };
      DisposablePromise.prototype = Object.create(Promise.prototype);
      DisposablePromise.prototype.constructor = DisposablePromise;
      DisposablePromise.prototype.then = function then(resolve, reject) {
          var returnVal = Promise.prototype.then.call(this, resolve, reject);
          return DisposablePromise.convert(returnVal);
      };
      DisposablePromise.prototype.catch = function _catch(err) {
          var returnVal = Promise.prototype.catch.call(this, err);
          return DisposablePromise.convert(returnVal);
      };
      DisposablePromise.prototype.finally = function finall(obj) {
          var returnVal = Promise.prototype.finally.call(this, obj);
          return DisposablePromise.convert(returnVal);
      };
      DisposablePromise.prototype.disposer = function disposer(disposerFunc) {
        var returnVal = Promise.prototype.finally.call(this, obj);
        return DisposablePromise.convert(returnVal);
      };

Но не было времени, чтобы доказать их.

Я думаю, что, возможно, стоит изучить другие варианты (сохранить bluebird, но преобразовать его для использования собственных промисов внутри?) из-за того, что эта функция должна быть в репозитории (если вы не можете придумать лучшие подходы... асинхронные итераторы? Хотелось бы услышать какие-либо мысли от команды bluebird даже по поводу абстрагирования этой функциональности, хотя мое чутье говорит, что это довольно сильно связано с крючками реализации bluebird.

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

@chaffeqa Np, спасибо, что вы все еще находите время, чтобы вернуться к этому!
Я очень сомневаюсь, что люди, работающие в bluebird, будут открыты для предложений серьезно переработать свою реализацию, они неоднократно повторяли, что на данный момент их интересует стабильность превыше всего, и они действительно рекомендуют людям использовать нативные промисы, если только им действительно не нужны расширенные функции. предоставлено Bluebird.
Учитывая, что сейчас Node 8 кажется самой популярной версией Node.js (судя по официальной статистике загрузок Node.js), я боюсь, что мы пока не можем перейти к подходу на основе асинхронного итератора.
Какие недостатки вы видите в том, что Knex реализует DisposablePromise внутри компании? Поскольку он расширяет собственный Promise, я предполагаю, что он не несет с собой никаких недостатков Bluebird, и ничего в пользовательском пространстве не нужно знать об этом?

@ericeslinger FWIW, типизация TS больше не должна быть проблемой в мастере, теперь мы печатаем наши промисы как нативные, чтобы отговорить пользователей от использования функций Bluebird. Это может вызвать проблемы в будущем, когда нативные обещания реализуют что-то, чего нет в обещаниях Bluebird, поэтому мы по-прежнему хотим максимально заменить используемые обещания. Любой вклад в этом направлении будет принят с благодарностью :)

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

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

Если в этой ветке есть кто-то еще, кто хотел бы заняться этим вопросом <3, давным-давно!

@chaffeqa Насколько сложна реализация Bluebird? Может быть, мы можем просто извлечь его и добавить поверх нативного обещания?

@chaffeqa Наихудший сценарий — мы можем удалить все другие варианты использования Bluebird и оставить этот из-за его сложности, если мы посчитаем его слишком рискованным. Не идеально, но в конечном итоге произойдет using .

к сожалению, довольно сложно ... реализация использует тот факт, что bluebird контролирует жизненный цикл обещания. Я думаю, что лучший подход — посмотреть, что он пытается сделать (что довольно близко к ссылке на using выше), и создать для него как можно более простую и производительную прокладку.

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

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

К вашему сведению, еще одна вещь, на мой взгляд: на самом деле минимальное использование usage и .disposer в knex, так что, может быть, подход работает лучше, чтобы перенести это на более высокий уровень?

Стоит эксперимент :)

оооо тоже вариант нашел на основе https://github.com/petkaantonov/bluebird/issues/1593

В любом случае, я думаю, что хорошим шагом вперед было то, что вы начали в предыдущей ветке, где мы изолировали все использование Promise , которое на самом деле является BluebirdPromise , таким образом, мы можем начать играть с отбрасыванием в заменах, таких как DisposablePromise или BluebirdNativePromise .

@chaffeqa Вы имеете в виду часть Bluebird.setScheduler(fn => Promise.resolve().then(fn)) ?
В целом преобразование проходит очень гладко! Если бы мы могли оставить Disposers в Bluebird, заставив их использовать собственные промисы под капотом, это могло бы стать хорошим решением.

Итак, эти все еще требуют внимания:
https://github.com/tgriesser/knex/issues/3257
https://github.com/tgriesser/knex/issues/3286
https://github.com/tgriesser/knex/issues/3256

Помощь будет принята с благодарностью!

Просто хотел также упомянуть, что неиспользование собственных промисов приводит к nodejs/node#22360 при использовании async_hooks , что приводит к потере текущего контекста.

Обходной путь — использовать патч https://github.com/TimBeyer/cls-bluebird .

Просто для информации: LTS для Node v8 заканчивается в этом году.

@Бессонов Контекст? Как увеличение минимального узла до 10 влияет на эту проблему? Обратите внимание, что мы уже отказались от поддержки Node 6.

Я не знаком с кодовой базой knex, но, возможно, есть некоторые функции, которые помогут вам избавиться от bluebird. Например, узел 10 поддерживает Promise.finally .

Но в любом случае, я рад видеть прогресс в этой теме :+1:

О шаблоне disposer — можем ли мы просто добавить необязательный обратный вызов для вещей, которые возвращают одноразовое обещание?
(Так же, как и с транзакциями)

getDisposableConnection(config, cb) {
    const connection = await getConnection(config)

   // user want autodisposable connection
    if (cb) 
      Promise.resolve(cb(connection)).then(() => connection.dispose())
   // user will dispose by himself
   return connection
}

Какой уровень независимости библиотек обещаний нам нужен?
1) все используют собственные промисы
2) внутренние обещания, пользователь может установить собственную библиотеку обещаний для интерфейса
3) пользователь может установить обещанную библиотеку для внутренних компонентов и интерфейса

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

Итак, чтобы ответить на исходный вопрос. Текущий обходной путь — просто подождать и добавить что-то вроде // tslint:disable-next-line: await-promise

@maximelkin Я голосую за вариант 1. Надеюсь, в долгосрочной перспективе каждая библиотека обещаний устареет.

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

@Bessonov в настоящее время работает с зависимыми от knex библиотеками (и, возможно, проектами), для которых требуется именно bluebird

мы должны дать им запасное решение

@Bessonov в настоящее время работает с зависимыми от knex библиотеками (и, возможно, проектами), для которых требуется именно bluebird, мы должны дать им какое-то резервное решение.

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

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

Но я полагаю, что все пользователи машинописного текста 1.5 теперь довольны.

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

По крайней мере, ранее версии knex 0.x рассматривались как основные выпуски с потенциально критическими изменениями, поэтому безопасным обновлением считалось только обновление до 0.20.x (semver действительно свободна, когда номер версии < 1).

Вопрос об удалении bluebird обсуждается уже давно, речь идет не только об этой проблеме.

удаление bluebird без причины

Удаление bluebird было не просто так. Вы по-прежнему можете внешне использовать bluebird с промисами knex. Одной из основных причин отказа от bluebird было то, что функции async неявно создают собственные промисы, поэтому в будущем использование Bluebird потребовало бы добавления дополнительного кода оболочки bluebird в knex API без какой-либо причины.

Без предупреждения, журнал изменений,

Согласованный. Я просмотрел последние журналы изменений... К сожалению, действительно похоже, что мы не смогли перечислить критические изменения между версиями. Нам нужно быть более осторожными при написании журналов изменений, чтобы действительно указывать на изменения, которые ломают старые API. Например, многие изменения типизации на самом деле сломают старый код TS.

Такая же проблема была в проекте ioredis https://github.com/luin/ioredis/commit/da60b8b. Они хотели поддерживать нативные промисы — и ребята сделали действительно хорошее решение — они добавили возможность поддержки любой пользовательской библиотеки промисов и по умолчанию используют нативные промисы. Почему бы нет? Настройка пользовательской библиотеки промисов выполняется быстро и не требует исправления всего кода приложения.

продолжение использования Bluebird потребовало бы добавления дополнительного кода оболочки bluebird в knex API без какой-либо причины.

Ага. Но почему бы не обернуть вызовы модулей в bluebird (или любую другую библиотеку промисов), если это было явно указано? Это одна простая оболочка, никаких накладных расходов, и это позволит пользователям использовать любую библиотеку промисов, которую они хотят. Если bluebird никому не нужен, никто не будет использовать эту опцию, и вы можете смело отказаться от нее со временем.

Также я видел мнение, что

Я надеюсь, что в долгосрочной перспективе каждая библиотека промисов устареет.

Но ИМХО есть два неверных предположения:

  • Bluebird используется, потому что он быстрее.
  • Bluebird используется в качестве полифилла.

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

Почему бы нет? Настройка пользовательской библиотеки промисов выполняется быстро и не требует исправления всего кода приложения.

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

@кубайт

Это не такое маленькое предприятие, как может показаться при первом осмотре.

Нет, это так же просто, как я уже сказал. Делаешь обертку для экспортируемых внешних функций и все. Около 10 строк кода. И пишите весь внутренний код как хотите.

@jehy : Не стесняйтесь отправлять PR для этих 10 строк кода, если вы видите простой способ их реализации.

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

Что бы это ни стоило, большая часть API bluebird дублируется с тем же или близким API с использованием собственных промисов этих пакетов: https://github.com/sindresorhus/promise-fun

Что бы это ни стоило, большая часть API bluebird дублируется с тем же или близким API с использованием собственных промисов этих пакетов.

~50 пакетов вместо 1? Серьезно?

Да, хотя в большинстве случаев требуются лишь некоторые из них (например, p-map). Конечно, ваш пробег может варьироваться. Он предлагается только как один из потенциальных путей к тому, что вы хотите.

@jehy : вот что вы можете попробовать в качестве временного обходного пути в коде вашего приложения:

const Bluebird = require('bluebird');


const prototypesNeedingDecoration = [
  require('knex/lib/query/builder').prototype,
  require('knex/lib/schema/builder').prototype,
  require('knex/lib/transaction').prototype,
  require('knex/lib/raw').prototype,
];

const corePromiseMethods = ["then", "catch", "finally"];


function decoratePromiseMethods(target) {
  for(const m of corePromiseMethods) {
    const original = target[m];

    target[m] = function(...args) {
      return Bluebird.resolve(original.apply(this, args))
    }
  }  
}

function hackBluebird() {
  for(const target of prototypesNeedingDecoration) {
    decoratePromiseMethods(target);
  }
}


hackBluebird();

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

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

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

С точки зрения нашего приложения, knex была последней библиотекой, заставившей нас требовать Bluebird, и соответствие полной встроенной поддержке промисов означает, что:

  1. у нас больше нет запятнанных трассировок стека
  2. мы уменьшили вес нашего SSR на приличную величину
  3. мы улучшили производительность, так как родной асинхронный ожидание теперь более производительный, чем bluebird (и растет все больше и больше!)

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

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

@chaffeqa Спасибо за этот отзыв, это очень важно!

@jehy : У вас была возможность попытаться обойти предложенное? Если да, то решило ли это ваши насущные проблемы?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

mattgrande picture mattgrande  ·  3Комментарии

npow picture npow  ·  3Комментарии

rarkins picture rarkins  ·  3Комментарии

nklhrstv picture nklhrstv  ·  3Комментарии

aj0strow picture aj0strow  ·  3Комментарии