2015-07-13 2 views
5

Пусть я создал или есть Node.js библиотека lib.jsPromisify импортировали класс (конструктор) с Bluebird в ES6 + Бабель

export class C { 
    constructor(value, callback) { 
     callback(false, `Hello ${value}`); 
    } 

    task(value, callback) { 
     callback(false, "returned " + value); 
    } 
} 

Важной частью является то, что конструктор классов должен принять обратный вызов, как это делает подключения к базе данных и ввода/вывода файлов. Если я сейчас импортирую и использую обратный стиль библиотеки, все в порядке (см. c1 ниже).

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

Тем не менее, я не могу найти способ new класс правильно в надежном обещании.

import Promise from 'bluebird'; 
import * as lib from './lib'; 


Promise.promisifyAll(lib); 


// old style -- works as expected 
const c1 = new lib.C("c1", (e, v) => { 
    console.log(c1, e, v); 
}); 


// assuming c1 got initialized, .task() also works 
c1.task("t1", console.log); 
c1.taskAsync("t2").then(() => console.log("also works")); 


// But how to do this properly with promises? 
const c2 = new lib.C("c2"); c2.then(console.log); // clearly doesn't work, lack of callback 
const c3 = new lib.CAsync("c3"); c3.then(console.log); // "cannot read property apply of undefined" 
const c4 = ??? 

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

+0

Не делать IO в конструкторах, что это плохая идея, чтобы связать Ио и строительство вместе. –

+0

По-твоему, вы, вероятно, правы. Но разве это не то, что делают узловые библиотеки Redis и многие другие библиотеки (неявно)? Кроме того, это не то, что я блокирую или что-то еще, просто вызывая действия. – left4bread

+1

@ left4bread Выполнение ввода-вывода (даже установление соединений) в вашем конструкторе, как правило, приводит вас к циклам инициализатора в короткие сроки. Просто разделение работы ввода-вывода на метод ('r = new Database(); r.openPool();') может сделать ваш код намного проще и более проверяемым. – ssube

ответ

6

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

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

export class C { 
    constructor(value, callback) { 
     callback(false, `Hello ${value}`); 
    } 

    task(value, callback) { 
     callback(false, "returned " + value); 
    } 
} 

И когда promisifying :

import Promise from 'bluebird'; 
import * as lib from './lib'; 


Promise.promisifyAll(lib); 

var old = lib.C; // reference the constructor 
lib.C = function(value){ // override it 
    o; // object we'll later return, populate in promise constructor 
    var p = new Promise(function(resolve, reject){ 
    // the promise constructor is always sync, so the following works 
    o = new old(value, function(err, data) { 
     if(err) return reject(err); 
     resolve(data); 
    }); 
    }); 
    // THIS IS THE IMPORTANT PART 
    o.then = p.then.bind(p); // make the object a thenable, 
    return o 
}; 

Который позволил бы вы оба использовать возвращаемое значение и обещание, обещание будет только then, так что вы можете Promise.resolve его, чтобы получить «реальный» обещание, а не объект со свойствами и обещание.

var o = new lib.C(); // get object 
o.then(function(data){ 
    // access data 
}); 

Это может быть извлечена на схеме:

function promisifyConstructor(cons){ 
    return function(...args) => { // new constructor function 
    let o; 
    let p = new Promise((resolve, reject) => { 
     // delegate arguments 
     o = new cons(...args, (err, data) => err ? reject(err) : resolve(data)); 
    }); 
    o.then = p.then.bind(p); 
    return o; 
    } 
} 
+0

В этом конкретном примере lib находится под моим контролем, однако потеря синхронизации инициализации будет уродливой. Тем не менее, я принимаю это, так как он убедил меня (и ссылку Берги выше), что я должен просто добавить метод «.init (callback)». – left4bread

+1

@ left4bread просто для того, чтобы быть ясным: решение здесь _exactly_, что вы просите, потеря синхронизации инициализации является побочным продуктом объединения и создания - я рекомендую против этого, это точно такая же проблема при использовании обратных вызовов. –

+0

@BenjaminGruenbaum Вам не хватает закрывающего пара в «новой старой» части. – neverfox

0

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

function createC(value) { 
    return new Promise(function (res, rej) { 
    var c = new C(value, function (err, val) { 
     if (err) { 
     rej(err); 
     } else { 
     res(val); // or res(c) if you prefer 
     } 
    }); 
    }); 
} 

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

+0

Вы решаете с разрешением o_0? –

+0

Также OP говорит, что он уже знает, как это сделать, так что это так. –

+0

@BenjaminGruenbaum Прокомментировал, почему фабрики лучше. Я решаю с разрешением, потому что конструктор принимает (потенциально асинхронный) обратный вызов; Есть ли способ лучше? – ssube

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