JavaScript - Vänta tills kommandot är utfört

Permalänk
Medlem

JavaScript - Vänta tills kommandot är utfört

Tjena!
Har en funktion som ser ut som följande:

function botWrite() { for(i=0;i<arguments.length;i++) { updateStatus(cache['botfname']+' is writing...'); var writeDelay = arguments[ i ].length * 100; writeDelay = writeDelay<800?800:writeDelay; setTimeout('botWriteTxt(\''+arguments[ i ]+'\')', writeDelay); } }

Och anropar funktionen såhär på ett ställe:

botWrite(introBot(), askForName(true));

Funktionen introBot() skriver ut t ex "Jag heter Kalle", medans askForName() skriver ut t ex "Vad heter du då?". Problemet här är att jag vill att det ska skapas en ny setTimeout när den nuvarande är färdig, dvs. att den ska bli:

Citat:

*skrivtid*
Jag heter Kalle
*skrivtid*
Vad heter du då?

Istället för:

Citat:

*skrivtid*
Jag heter Kalle
Vad heter du då?

Om något är oklart så fråga gärna.
Tack på förhand!

Permalänk
Medlem

Hur ska

botWrite(introBot()); botWrite(askForName(true));

bete sig då? (Dvs. ska den bry sig om att det redan kanske finns saker på på kö att skriva ut innan?)

Permalänk
Medlem

Jag ändrade funktionen just för att jag trodde det skulle bli lättare så. Som du säkert anat så ska det bli en bot, så när jag skriver något så ska den skriva tillbaka. Och i det här fallet ska den fråga om ens namn när man har frågat om hans. Vet inte alls hur jag ska lösa det för tillfället.

Permalänk
Medlem

Det bästa är nog om du konstruerar en kö för output från boten.... Ha en funktion i stil med addToOutputQueue(text) som lägger texten på kön och som resetar delayen mellan varje output baserat på hur lång texten är...

setTimeout används ju då för automatiskt anrop...

Permalänk
Medlem

Förstår ungefär hur du menar. Har en funktion som heter botWriteTxt():

function botWriteTxt(inputstr) { updateStatus(' '); inputText('<tr class="bot"><th>He says:</th>'); inputText('<td>'+inputstr+'</td></tr>'); addToRecentBot(inputstr); justSpoken = false; }

Kan jag konstruera om den (eller botWrite()) så att den fungerar på det sättet du beskrev? Eller om du kunde skriva ett eget litet exempel.

Permalänk
Medlem

Jag har ingen presenterbar vision i huvudet, men jag tänker mig att du har en array med kommande texter, som läggs på löpande.

Sedan tar du hela tiden det äldsta meddelandet, skriver ut det, raderar det ur listan och sätter en timeout som skriver ut nästa text (timeoutens tid baseras på textens längd)... Så låter du den jobba så med timeout hela tiden... När arrayen är tom så väntar den naturligtvis på att nytt material ska tryckas in, beroende på vad användaren skriver, och vad AIn tar fram för text att skriva ut.

Förfarandet är alltså:

1: Användare skriver text
2: AI tolkar texten och bestämmer ett svar
3: Svaret läggs i din outputarray
4: Outputarrayen plockar en rad i taget från botten och fördröjer dem i tid baserat på textens längd.

Permalänk
Medlem

Okej, men om vi nu skulle skapa en kod som ser ut ungefär som följande:

var messages = new Array(); function addToMsgQueue(inputstr) { messages.push(inputstr); } function loopMsgQueue() { for(i=0;i=messages.length;i++) { botWrite(messages[i]); } }

Där botWrite() ser ut som jag tidigare skrivit. Då skulle ju inte for-loopen vänta tills timeouten är färdig, utan skulle istället kasta iväg alla timeouts på en gång (så som det är nu).

Permalänk
Medlem

du ska inte ha en loop för outputen, du ska plocka ut första raden ur arrayen, skriva ut den, sen sätta en timeout som framöver postar nästa rad.

Du ska hela tiden ha en timeout som matar till botWrite, aldrig någon loop.

Permalänk
Medlem

Förstår inte alls hur det skulle gå till. Skriv ett exempel, snälla.

Permalänk
Medlem

har du googlat fram hur settimeout och timeouts fungerar? Då kan du få funktioner att anropas "automatiskt" i vissa tidsintervall... Vid varje sådant anrop så skriver du ut en text ur din array.

Första elementet i din array når du genom minArray[0] och hur man sedan tar bort det efter man skrivit ut det vet jag inte, men googla lite på typ "javascript array push pop" så kommer du säkert hitta lite godis för hur man leker med arrayer på smidiga sätt.

Permalänk
Medlem

Just med min array har jag inga som helst problem med, det är just det där med tiden. Tack för tipset om setInterval() (som jag antog att du menade), känns som att man kom ett steg närmare.
Problemet då är ju att texterna kan vara olika långa, och bör då ta olika lång tid att skriva ut.

Permalänk
Medlem

Precis, och det löser du genom att titta hur lång nästkommande sträng är (minArray[0].length) och multiplicera längden med typ 0.2 eller nåt för att få en vettig tid...

Så varje gång du sätter en ny timeout så tittar du hur långt det kommande meddelandet är för att få en rimlig fördröjning.

Permalänk
Medlem

Testade lite:

function queue() { var stack = []; // Inte som mål att skapa en effektiv stack, endast att dess metoder ska ha så lite kod som möjligt. :) return { pop: function() { return stack.shift(); }, front: function() { return stack[stack.length - 1]; }, push: function(value) { stack.push(value); }, empty: function() { return !stack.length; } }; } function bot() { var q = queue(); var max_delay = 1000, time_per_char = 100; function write() { var obj = q.pop(); // *skriv ut obj på något sätt* if (!q.empty()) setTimeout(write, q.front().delay); } return { say: function() { var empty = q.empty(); var len = arguments.length; if (len == 0) return; for (var i = 0; i < len; i++) q.push({msg: arguments[i], delay: Math.min(max_delay, arguments[i].length * time_per_char)}); if (empty) setTimeout(write, q.front().delay); } }; } window.onload = function() { var b = bot(); b.say("hej hej ...", "vad gör du? ..."); b.say("hohoho ...", "höhöhö ..."); };

Redigering: js2-mode indenterade roligt.

Redigering två: justerade en liten sak i say bara.

Permalänk
Medlem

totoo: Ahaa, så då skulle man kunna köra något liknande det här?

function botWrite() { for(i=0;i<arguments.length;i++) { updateStatus(cache['botfname']+' is writing...'); var writeDelay = 0; arguments[ i ].length * 100; for(x=i; x<arguments.length;x++) { var tempDelay = Math.max(800, arguments[x].length); writeDelay += arguments[x].length; } setTimeout('botWriteTxt(''+arguments[ i ]+'')', writeDelay); } }

cic: Aldrig sett någon liknande kod. Fick [object Object="Object"][object Object="Object"][object Object="Object"][object Object="Object"] som output.

Permalänk
Medlem

alltså du ska aldrig ha en loop när du ska skriva ut text. Du ska ju faktiskt bara skriva ut ett inlägg i taget (och en massa annat ska hända under tiden).

Du ska bara först anropa din utskriftsfunktion, sedan ska den sätta en timeout för att anropa sig själv igen efter en viss tid (beroende på hur långt det kommande inlägget är).

Aldrig någon loop för utskrift!

edit: eller så använder du din loop till något vettigt, jag har inte tid att titta så noga på djupet, måste rusa!

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av Ivarska
Aldrig sett någon liknande kod.

Lite Closure och Javascript-objekt endast. För att skriva ut det på ett vettigare sätt får du göra en egen toString eller helt enkelt skriv ut dess medlemmar direkt: obj.msg osv. Sedan kanske totoos idé inte är helt tokig, behöver ju trots allt inte spara tiden det tar för botten att skriva texten i stacken. (Den sparas där i eftersom att jag trodde jag skulle klara mig utan front-funktionen i stacken, men det behövdes ändå i slutet. )

Kom ihåg att använda var på alla variabler du inte vill ha globala och mata inte setTimeout med en stäng, ge den en funktion istället.

Eller om du vill testa med din lösning så borde du flytta in setTimeout i loopen, och andra mindre ändringar på delay-variablerna.

Permalänk
Medlem

Något i stil med det här kanske?

var botWrite = (function() { var queue = { stack: [], next: function() { if (this.stack.length > 0) { var msg = this.stack.shift(); setTimeout(function() { console.log(msg); // eller botWriteTxt(msg) queue.next(); }, msg.length>8 ? msg.length*100:800); } } }; return function() { for (var i = 0; i < arguments.length; i++) { queue.stack.push(arguments[i]); } if (arguments.length == queue.stack.length) { queue.next(); } } })();

Snarlikt cic kod, bara lite förenklat och rakt på sak.