3847 Stimmen

Was ist die JavaScript-Version von sleep()?

Gibt es einen besseren Weg zur Entwicklung einer sleep in JavaScript als die folgenden pausecomp Funktion ( entnommen von hier )?

function pausecomp(millis)
{
    var date = new Date();
    var curDate = null;
    do { curDate = new Date(); }
    while(curDate-date < millis);
}

Es handelt sich nicht um ein Duplikat von Sleep in JavaScript - Verzögerung zwischen Aktionen Ich möchte eine echter Schlaf in der Mitte einer Funktion und nicht eine Verzögerung, bevor ein Teil des Codes ausgeführt wird.

2voto

timrs2998 Punkte 1293

Auf naive Weise können Sie Folgendes implementieren sleep() mit einer while-Schleife wie bei der Funktion pausecomp (dies ist im Grunde dasselbe):

const sleep = (seconds) => {
    const waitUntil = new Date().getTime() + seconds * 1000
    while(new Date().getTime() < waitUntil) {
        // do nothing
    }
}

Und Sie können die sleep() Methode wie folgt:

const main = () => {
    const a = 1 + 3

    // Sleep 3 seconds before the next action
    sleep(3)
    const b = a + 4

    // Sleep 4 seconds before the next action
    sleep(4)
    const c = b + 5
}

main()

So stelle ich mir vor, dass Sie die Schlaffunktion verwenden, und sie ist relativ einfach zu lesen. Ich habe es aus dem anderen Beitrag entlehnt Sleep in JavaScript - Verzögerung zwischen Aktionen um zu zeigen, wie Sie es möglicherweise verwenden wollten.

Leider wird Ihr Computer dann warm und alle Arbeiten werden blockiert. Wenn das Programm in einem Browser ausgeführt wird, kommt die Registerkarte zum Stillstand und der Benutzer kann nicht mit der Seite interagieren.

Wenn Sie Ihren Code so umstrukturieren, dass er asynchron ist, dann können Sie die setTimeout() als Schlaffunktion wie in dem anderen Beitrag.

// define sleep using setTimeout
const sleep = (seconds, callback) => setTimeout(() => callback(), seconds * 1000)

const main = () => {
    const a = 1 + 3
    let b = undefined
    let c = undefined

    // Sleep 3 seconds before the next action
    sleep(3, () => {
        b = a + 4

        // Sleep 4 seconds before the next action
        sleep(4, () => {
            c = b + 5
        })
    })
}

main()

Wie Sie schon sagten, ist es nicht das, was Sie wollten. Ich habe das Beispiel aus Sleep in JavaScript - Verzögerung zwischen Aktionen um zu zeigen, warum das so sein könnte. Wenn Sie weitere Aktionen hinzufügen, müssen Sie entweder Ihre Logik in separate Funktionen aufteilen oder Ihren Code immer tiefer verschachteln ( Rückrufhölle ).

Um die "Callback-Hölle" zu lösen, können wir den Schlaf mit verspricht stattdessen:

const sleep = (seconds) => new Promise((resolve => setTimeout(() => resolve(), seconds * 1000)))

const main = () => {
    const a = 1 + 3
    let b = undefined
    let c = undefined

    // Sleep 3 seconds before the next action
    return sleep(3)
        .then(() => {
            b = a + 4

            // Sleep 4 seconds before the next action
            return sleep(4)
        })
        .then(() => {
            c = b + 5
        })
}

main()

Promises können die tiefe Verschachtelung vermeiden, sehen aber immer noch nicht wie der reguläre synchrone Code aus, mit dem wir begonnen haben. Wir wollen einen Code schreiben, der synchron aussieht, aber keine der Nachteile hat.

Schreiben wir unsere Hauptmethode noch einmal um, indem wir async/await :

const sleep = (seconds) => new Promise((resolve => setTimeout(() => resolve(), seconds * 1000)))

const main = async () => {
    const a = 1 + 3

    // Sleep 3 seconds before the next action
    await sleep(3)
    const b = a + 4

    // Sleep 4 seconds before the next action
    await sleep(4)
    const c = b + 5
}

main()

Mit async/await können wir Folgendes aufrufen sleep() fast so, als wäre es eine synchrone, blockierende Funktion. Dies löst das Problem, das Sie möglicherweise mit der Callback-Lösung aus dem anderen Beitrag hatten und vermeidet Probleme mit einer lang laufenden Schleife.

2voto

Guang-De Lin Punkte 1

Mit await Unterstützung und Blaupausenversprechen :

await bluebird.delay(1000);

Dies funktioniert wie eine synchrone sleep(1) der C-Sprache . Meine Lieblingslösung.

2voto

tponthieux Punkte 1416

Sie können einen Closure-Aufruf setTimeout() mit inkrementell größeren Werten verwenden.

var items = ['item1', 'item2', 'item3'];

function functionToExecute(item) {
  console.log('function executed for item: ' + item);
}

$.each(items, function (index, item) {
  var timeoutValue = index * 2000;
  setTimeout(function() {
    console.log('waited ' + timeoutValue + ' milliseconds');
    functionToExecute(item);
  }, timeoutValue);
});

Ergebnis:

waited 0 milliseconds
function executed for item: item1
waited 2000 milliseconds
function executed for item: item2
waited 4000 milliseconds
function executed for item: item3

2voto

Abdennour TOUMI Punkte 75271

Wenn Sie einen Rat möchten, um nicht an Leistung zu verlieren. setTimeout ist Ihre erwartete sleep . Wenn Sie jedoch eine Syntax wünschen, bei der der Code "in der Mitte geteilt" wird durch sleep können wir tun:

sleep = function(tm, fn){
   window.setTimeout(fn, tm);
}

Bereiten Sie dann die Funktionen wie im Folgenden beschrieben vor:

var fnBeforeSleep = function(){

  // All code before sleep

}

var fnAfterSleep = function(){

  // All code after sleep

}

Dann:

fnBeforeSleep();
sleep(2000, fnAfterSleep);

// Yep! Syntactically, it is very close to:

fnBeforeSleep();
sleep(2000);
fnAfterSleep();

2voto

JonnyRaa Punkte 6818

Machen Sie sich die asynchrone Natur von JavaScript zu eigen!

Alle folgenden geben sofort zurück, haben aber einen einzigen Platz für den Code, den Sie ausführen möchten, nachdem etwas passiert ist.

Die Methoden, die ich hier skizziert habe, sind alle für unterschiedliche Anwendungsfälle gedacht und sind grob nach ihrer Komplexität geordnet.

Die verschiedenen Dinge sind wie folgt:

  • Warten, dass eine Bedingung erfüllt wird
  • Warten auf den Abschluss einer Reihe von Methoden (in beliebiger Reihenfolge), bevor ein einzelner Rückruf aufgerufen wird
  • Ausführung einer Reihe von asynchronen Methoden mit gemeinsamem Status in einer bestimmten Reihenfolge vor dem Aufruf eines Rückrufs

Warten

Warten, um zu sehen, ob eine Bedingung wahr ist, ist nützlich, wenn es keinen zugänglichen Rückruf gibt, um Ihnen mitzuteilen, wann etwas die Ausführung beendet hat.

Dies ist eine ziemlich einfache Implementierung, die davon ausgeht, dass die Bedingung zu einem bestimmten Zeitpunkt wahr wird. Mit ein paar Anpassungen könnte sie noch nützlicher werden (z. B. durch die Festlegung eines Anruflimits). (Ich habe dies erst gestern geschrieben!)

function waitFor(predicate, successCallback) {
    setTimeout(function () {
        var result = predicate();
        if (result !== undefined)
            successCallback(result);
        else
            waitFor(predicate, successCallback);
    }, 100);
}

Aufrufender Code:

beforeEach(function (done) {
    selectListField('A field');

    waitFor(function () {
        var availableOptions = stores.scrapeStore(optionStore);
        if (availableOptions.length !== 0)
            return availableOptions;
    }, done);
});

Hier rufe ich etwas auf, das eine Ext JS speichern' und warten, bis der Speicher etwas enthält, bevor sie fortfahren (die beforeEach ist eine Jasmin Test-Framework).

Warten, bis mehrere Dinge abgeschlossen sind

Ich musste auch einen einzigen Rückruf ausführen, nachdem eine Reihe von verschiedenen Methoden beendet wurde. Sie können das wie folgt tun:

createWaitRunner = function (completionCallback) {
    var callback = completionCallback;
    var completionRecord = [];
    var elements = 0;

    function maybeFinish() {
        var done = completionRecord.every(function (element) {
            return element === true
        });

        if (done)
            callback();
    }

    return {
        getNotifier: function (func) {
            func = func || function (){};

            var index = elements++;
            completionRecord[index] = false;

            return function () {
                func.applyTo(arguments);
                completionRecord[index] = true;
                maybeFinish();
            }
        }
    }
};

Aufrufender Code:

var waiter = createWaitRunner(done);

filterList.bindStore = waiter.getNotifier();
includeGrid.reconfigure = waiter.getNotifier(function (store) {
    includeStore = store;
});

excludeGrid.reconfigure = waiter.getNotifier(function (store) {
    excludeStore = store;
});

Sie können entweder nur auf die Benachrichtigungen warten oder auch andere Funktionen einschließen, die die an die Funktion übergebenen Werte verwenden. Wenn alle Methoden aufgerufen werden, dann done ausgeführt werden.

Asynchrone Methoden der Reihe nach ausführen

Ich habe einen anderen Ansatz verwendet, wenn ich eine Reihe von asynchronen Methoden in einer Reihe aufrufen musste (wiederum in Tests). Dies ist etwas ähnlich wie etwas, das man in die Async-Bibliothek - Serie macht in etwa das Gleiche, und ich habe mir diese Bibliothek zuerst ein wenig durchgelesen, um zu sehen, ob sie das tut, was ich will. Ich denke, meine hat eine schönere API für die Arbeit mit Tests aber (und es hat Spaß gemacht, zu implementieren!).

// Provides a context for running asynchronous methods synchronously
// The context just provides a way of sharing bits of state
// Use 'run' to execute the methods.  These should be methods that take a callback and optionally the context as arguments
// Note the callback is provided first, so you have the option of just partially applying your function to the arguments you want
// instead of having to wrap even simple functions in another function

// When adding steps you can supply either just a function or a variable name and a function
// If you supply a variable name then the output of the function (which should be passed into the callback) will be written to the context
createSynchronisedRunner = function (doneFunction) {
    var context = {};

    var currentPosition = 0;
    var steps = [];

    // This is the loop. It is triggered again when each method finishes
    var runNext = function () {
        var step = steps[currentPosition];
        step.func.call(null,
                       function (output) {
                           step.outputHandler(output);
                           currentPosition++;

                           if (currentPosition === steps.length)
                               return;

                           runNext();
                       }, context);
    };

    var api = {};

    api.addStep = function (firstArg, secondArg) {
        var assignOutput;
        var func;

        // Overloads
        if (secondArg === undefined) {
            assignOutput = function () {
            };
            func = firstArg;
        }
        else {
            var propertyName = firstArg;
            assignOutput = function (output) {
                context[propertyName] = output;
            };
            func = secondArg;
        }

        steps.push({
            func: func,
            outputHandler: assignOutput
        });
    };

    api.run = function (completedAllCallback) {
        completedAllCallback = completedAllCallback || function(){};

        var lastStep = steps[steps.length - 1];
        var currentHandler = lastStep.outputHandler;
        lastStep.outputHandler = function (output) {
            currentHandler(output);
            completedAllCallback(context);
            doneFunction();
        };

        runNext();
    };

    // This is to support more flexible use where you use a done function in a different scope to initialisation
    // For example, the done of a test but create in a beforeEach
    api.setDoneCallback = function (done) {
        doneFunction = done;
    };

    return api;
};

Aufrufender Code:

beforeAll(function (done) {
    var runner = createSynchronisedRunner(done);
    runner.addStep('attachmentInformation', testEventService.getAttachmentCalled.partiallyApplyTo('cat eating lots of memory.jpg'));
    runner.addStep('attachment', getAttachment.partiallyApplyTo("cat eating lots of memory.jpg"));
    runner.addStep('noAttachment', getAttachment.partiallyApplyTo("somethingElse.jpg"));
    runner.run(function (context) {
        attachment = context.attachment;
        noAttachment = context.noAttachment;
    });
});

TeilweiseAnwendenZum ist im Grunde eine umbenannte Version von Douglas Crockford die Umsetzung von Curry. Vieles von dem, womit ich arbeite, nimmt einen Rückruf als letztes Argument, so dass einfache Aufrufe auf diese Weise durchgeführt werden können, anstatt alles mit einer zusätzlichen Funktion zu verpacken.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X