2017-08-28 2 views
0

Ich habe eine Klasse, die db-Methoden enthält und es ist in einem Proxy eingewickelt, der den Zugriff auf Eigenschaften behandelt. Da das Thema verspricht hier bezogen wird, ist ein vereinfachtes Beispiel-Code, der das gleiche Problem reproduziert:Versprechen als Klassenmethode Aufruf triggert object.then beim Auflösen

const handler = { 
    ownKeys(target) { 
    return Object.keys(target._attributes) 
    }, 
    get(target, property) { 
    console.log(`<-${property}`) // <-- this logs what properties are being accessed 
    if (typeof target[property] !== 'undefined') { 
     return Reflect.get(target, property) 
    } 
    return Reflect.get(target._attributes, property) 
    }, 
    set(target, property, value) { 
    target._attributes[property] = value 
    return true 
    } 
} 
class User { 
    static table = 'users' 
    static fetch(query = {}, opts = {}) { 
     return new Promise((resolve, reject) => { 
      setTimeout(() => { 
      resolve(new this(query)) 
      }, 500) 
     }) 
    } 
    constructor(attributes = {}) { 
     this._attributes = attributes 
     return new Proxy(this, handler) 
    } 
} 
async function trigger() { 
    const user = await User.fetch({test:'test'}) 
    console.log(JSON.stringify(user._attributes)) 
} 
trigger() 

Alles funktioniert gut, während der Test ich einen Ausdruck an den Proxy hinzugefügt haben Performance-Einbußen der Verwendung eines solchen zu bestimmen, Modellentwurf, und ich bemerkte, dass mein Modell aus der Versprechungskette heraus aufgerufen wird.

Beispielausgabe folgt:

<-then 
<-_attributes 
{"test":"test"} 

Ich denke, dass new this(query) Rückkehr die Versprechungen verursacht vielleicht zu denken, dass es ein Versprechen zurück und folglich .then() ausgeführt wird. einzige Lösung, die ich gefunden habe, ist, wie diese Entschlossenheit Antwort innerhalb neues Array oder ein anderes Objekt zu wickeln:

static fetch(query = {}, opts = {}) { 
    return new Promise((resolve, reject) => { 
    setTimeout(() => { 
     resolve([new this(query)]) 
    }, 500) 
    }) 
} 
// Output 
// 2 
// <-_attributes 
// {"test":"test"} 

Was ich frage mich, ist dies richtige Weg, und es gibt auch andere Lösungen für diese Nebenwirkung?

+1

Was genau stimmt nicht mit dem Verhalten, das Sie jetzt sehen? Der Wert, der an "resolve" übergeben wird, kann ein "thenable" sein, also prüft er, ob '.then 'existiert. Das wird erwartet. – loganfsmyth

+0

Es ist nichts falsch, ich habe mich nur gefragt, ob dies erwartet Verhalten ist und es ist ein Weg um es herum. Das ist, dass ich unnötige Aufrufe an Proxy verhindern wollte, da seine Leistung in einigen Fällen fraglich ist. –

+0

Die Lösung mit "*' new this (query) "bewirkt, dass die Versprechen zu denken, dass es vielleicht ein Versprechen zurückgegeben *" - ja, genau. "... und folglich' .then() 'wird ausgeführt *" - nein, es wird nicht ausgeführt, weil es nicht existiert. Aber es wird zugegriffen, um * es * zu versuchen, es auszuführen, falls es eine Methode ist. – Bergi

Antwort

1

Alle Objekte, die als Ergebnis eines Versprechens übergeben werden, werden immer daraufhin überprüft, ob sie eine then Eigenschaft besitzen. Wenn dies der Fall ist, wird diese Funktion verwendet, um Einträge in die Warteschlange zu stellen, um einen endgültigen Wert zu erhalten. Aus diesem Grund Logik wie

Promise.resolve() 
    .then(() => { 
     return Promise.resolve(45); 
    }) 
    .then(result => { 
     console.log(result); 
    }); 

logs 45 anstelle eines Versprechens Objekt. Da die Promise-Objekte eine .then-Eigenschaft haben, wird sie verwendet, um den Versprechenswert auszupacken. Das gleiche Verhalten passiert in Ihrem Fall resolve(new this(query)), weil es wissen muss, ob es einen Wert zum Auspacken gibt.

Wie Sie gesagt haben, in Ihrem Beitrag kommentiert, könnten Sie sicherlich die Instanz mit einem Nicht-Proxy, z.

resolve({ value: new this(query) }) 

die für .then auf dass Objekt überprüfen würde, anstatt auf Ihrem Proxy, aber dann haben Sie .value zu tun, um tatsächlich den Proxy zu bekommen, die ein Schmerz sein kann.

Am Ende des Tages, das ist eine Wahl, die Sie treffen müssen.

+0

Ich werde deine Antwort akzeptieren, da sie etwas Licht auf das Problem wirft Hand.Ich habe so etwas vermutet, aber es ist immer gut, eine Bestätigung zu haben. Wie auch immer, ich werde einfach einen Destruktor auf das erwartete Versprechen setzen und das wird den Trick ohne hässliches .value machen. Zum Beispiel: const [user] = await User.fetch ({username}) –

Verwandte Themen