2017-02-09 9 views
0

Hat der folgende JavaScript-Code undefiniertes Verhalten?

var resolve; 
 

 
var head = { next: new Promise(r => resolve = r) }; 
 

 
function addData(d) { 
 
    resolve({ 
 
     data: d, 
 
     next: new Promise(r => resolve = r) 
 
    }); 
 
}

schrieb ich den obigen Code etwas ähnliches zu verketteten Liste zu erreichen, während die Daten in der Liste asynchron geladen wird.

Der Kopf dieser "verknüpften Liste" ist head. Jeder Knoten in der Liste hat zwei Felder, .data und .next, wie eine normale verknüpfte Liste. .next ist ein Versprechen, das zum nächsten Knoten in der Liste aufgelöst wird.

Jedes Mal, wenn addData(...) aufgerufen wird, wird das Feld .next des letzten letzten Knotens in der Liste in einen neuen Knoten aufgelöst und somit zum neuen letzten Knoten.

Ich habe die Funktionalität des obigen Codes in Node.js verifiziert und es funktioniert wie erwartet. Hier ist der Code, den ich um das Verhalten zu überprüfen verwenden:

var resolve; 
 
var head = { next: new Promise(r => resolve = r) }; 
 
function addData(d) { resolve({ data: d, next: new Promise(r => resolve = r) }); } 
 

 
async function verify() { 
 
    while(true) { 
 
     head = await head.next; 
 
     console.log(head.data); 
 
    } 
 
} 
 

 
verify(); 
 
addData(1); // outputs: 1 
 
addData(2); // outputs: 2 
 
addData(3); // outputs: 3

Allerdings bin ich mir nicht sicher, ob es irgendwelche potenzielles Problem (Speicher, Effizienz) mit dieser Struktur. Auch ich bin besonders besorgt über diese Linie:

resolve({data: d, next: new Promise(r => resolve = r})

wo Entschlossenheit genannt wird und zugleich zugeordnet. Was sollte zuerst geschehen, Zuordnung oder Funktionsnamen Auflösung? Ist das ein undefiniertes Verhalten?

Vielen Dank!

+1

Ob es funktioniert oder nicht, es ist sicher stumpf (schwer zu lesen und den Punkt des Codes zu verstehen). – jfriend00

+0

@ jfriend00 Er würde nur die ganze Kette behalten, wenn er einen Verweis auf den Anfangskopf behielt, was er nicht tut. Natürlich ist es leicht falsch zu verstehen ... – Bergi

+0

@Bergi - Ich hatte diesen Kommentar bereits gelöscht, weil ich festgestellt habe, dass das zu schwer zu verstehen ist, was vor sich geht (schlechter Code meiner Meinung nach aus diesem Grund). Aber das OP sagte "wie eine verknüpfte Liste", aber ich denke, es ist überhaupt keine verknüpfte Liste. Ich denke, es ist nur ein höherer Bereich des vorherigen Versprechens. Gibt es nicht einen viel, viel besseren Weg, dies zu tun, der nicht so stumpf ist und nicht die höher definierte Variable 'resolve' verwendet? – jfriend00

Antwort

0

Was soll zuerst geschehen, Zuordnung oder Funktion Name Auflösung?

Funktionsname Auflösung geschieht, bevor die Bewertung der Argumente (das Objekt Literal, das die Promise Konstruktoraufruf enthält, die wiederum die Callback aufruft, die die Zuweisung enthält).

Ist dies ein undefiniertes Verhalten?

Nein. Aber ein Code-Geruch.

Gibt es einen besseren/eleganteren Weg, dies zu tun?

Ja. Es hängt hauptsächlich davon ab, wie die Datenstruktur generiert wird. Wenn Sie einen funktionalen Ansatz verwenden, würde ich eine rekursive Funktion empfehlen:

function getStream(i) { 
    return new Promise(resolve => { 
     setTimeout(resolve, 100); 
    }).then(() => ({ 
     data: i, 
     next: getStream(i+1) 
    })); 
} 
(async function() { 
    for (var data, next, head = getStream(1); {data, next} = await head; head = next) { 
     console.log(data); 
    } 
}()); 

Wenn Sie einen dringend notwendigen Ansatz verwenden, wenn jemand anderes die Daten erzeugt, würde ich die Liste rufe eine asynchrone Warteschlange und setze in eine geeignete Struktur, aber Ihr addData Code ist in Ordnung (außer für die Verwirrung über resolve, die Sie mit einer zusätzlichen Variablen verwalten können).

+0

Danke für die Antwort! Wenn ich diese "verknüpfte-Liste" -artige Struktur implementieren muss, gibt es dann irgendeinen besseren/eleganteren Weg dies zu tun? –

0

Wie Bergi darauf hinweist, ist dies kein undefiniertes Verhalten, aber es gibt Möglichkeiten, dies zu implementieren, die nicht ganz so verwirrend sind.

Zum Beispiel könnten Sie die addData Funktion schreiben, so dass es nicht die resolve Funktion und rufen Sie es in derselben Anweisung nicht zuordnen:

function addData(d) { 
    let newResolve; 

    resolve({ data: d, next: new Promise(r => newResolve = r) }); 

    resolve = newResolve; 
} 

Es sieht aus wie Sie einen sehr Kreisverkehr Ansatz könnte mit zu Erstellen eines Streams oder einer beobachtbaren Sammlung. In diesem Fall ist es möglicherweise besser, eine vorhandene Implementierung einer dieser Komponenten zu verwenden.

+0

Wie ist das Speichern einer Deferred besser als das Speichern einer 'resolve' Funktion? – Bergi

+0

@Bergi Es scheint mir ein bisschen klarer, aber ein guter Punkt. Ich habe meine Antwort geändert, um die verwirrende Zeile zu vermeiden, die in derselben Anweisung "resolve" aufruft und zuweist. – JLRishe

+0

Vielen Dank für die Antwort und Vorschläge! Die Anwendung ist wirklich kompliziert, um in wenigen Worten zu erklären. Ich werde versuchen, eine vorhandene Implementierung zu finden, die der Anforderung entspricht .... –

Verwandte Themen