Tidur dalam JavaScript - penundaan antar tindakan

129

Apakah ada cara saya bisa tidur di JavaScript sebelum melakukan tindakan lain?

Contoh:

 var a = 1+3;
 // Sleep 3 seconds before the next action here
 var b = a + 4;
Jin Yong
sumber

Jawaban:

140

Anda dapat menggunakan setTimeoutuntuk mencapai efek yang serupa:

var a = 1 + 3;
var b;
setTimeout(function() {
    b = a + 4;
}, (3 * 1000));

Ini tidak benar-benar 'tidur' JavaScript — itu hanya menjalankan fungsi yang diteruskan setTimeoutsetelah durasi tertentu (ditentukan dalam milidetik). Meskipun dimungkinkan untuk menulis fungsi tidur untuk JavaScript, sebaiknya gunakan setTimeoutjika memungkinkan karena tidak membekukan segalanya selama periode tidur.

Steve Harrison
sumber
9
Lihat juga pada setInterval (). Ini mirip dengan setTimeout (), tetapi fungsi Anda dipanggil beberapa kali (sampai Anda menghentikannya), yang berguna jika Anda ingin melakukan sesuatu saat tidur (seperti melakukan pembaruan kemajuan, menjaga beberapa kondisi internal, atau apa pun).
Anders Sandvig
5
Ini tidak menjawab pertanyaan. Pertanyaannya meminta setara "tidur" yang ini bukan.
felwithe
Meskipun jawaban ini tidak sesuai dengan pertanyaan yang diajukan, tetapi lebih bermanfaat daripada loop dan membandingkan Date.now (). Tidak ada yang harus menggunakan loop yang diblokir, sleep implement.
Li Chunlin
2
Kecuali, tentu saja, loop memblokir adalah persis apa yang diinginkan seseorang.
Wonko the Sane
55

Jika Anda benar-benar membutuhkan sleep()hanya untuk menguji sesuatu. Namun ketahuilah bahwa itu akan membuat browser macet sebagian besar saat debuggin - mungkin karena itulah Anda tetap membutuhkannya. Dalam mode produksi, saya akan mengomentari fungsi ini.

function pauseBrowser(millis) {
    var date = Date.now();
    var curDate = null;
    do {
        curDate = Date.now();
    } while (curDate-date < millis);
}

Jangan gunakan new Date()dalam loop, kecuali jika Anda ingin membuang-buang memori, kekuatan pemrosesan, baterai dan mungkin seumur hidup perangkat Anda.

Rodrigo
sumber
8
Jawaban ini layak mendapat lebih banyak suara. Dibintangi pertanyaan hanya karena jawaban ini.
jagc
bagaimana dengan peringatan "terlalu banyak rekursi"?
Oki Erie Rinaldi
1
@OkiErieRinaldi Tidak ada rekursi di sana, hanya ada satu putaran.
Rodrigo
7
@ 3.1415926535897932384626433833 Nah, seseorang meminta fungsi "tidur", itulah yang ada di sini. Saya pernah menggunakannya, tidak ingat persis untuk jenis debugging apa. Jika saya membutuhkannya lagi, saya tahu persis di mana menemukannya. Jika Anda lebih suka fungsi lain, itu pilihan Anda. Bukankah hebat bisa memilih?
Rodrigo
2
"Sibuk menunggu".
Zeek2
13

Versi ECMAScript 6, menggunakan generator dengan hasil untuk "pemblokiran kode":

Karena pertanyaan awal telah diposting tujuh tahun yang lalu, saya tidak repot-repot menjawab dengan kode yang tepat, karena terlalu mudah dan sudah dijawab. Ini akan membantu dalam masalah yang lebih rumit, seperti jika Anda memerlukan setidaknya dua tidur, atau jika Anda berencana untuk mengurutkan eksekusi yang tidak sinkron. Jangan ragu untuk memodifikasinya agar sesuai dengan kebutuhan Anda.

let sleeptime = 100
function* clock()
{
    let i = 0
    while( i <= 10000 )
    {
        i++
        console.log(i); // actually, just do stuff you wanna do.
        setTimeout(
            ()=>
            {
                clk.next()
            }
            , sleeptime
        )
        yield
    }
}

let clk = clock()
clk.next()

fungsi*

() => fungsi panah

Anda juga dapat mengaitkan acara melalui Janji :

function sleep(ms)
{
    return(
        new Promise(function(resolve, reject)
        {
            setTimeout(function() { resolve(); }, ms);
        })
    );
}


sleep(1000).then(function()
{
    console.log('1')
    sleep(1000).then(function()
    {
        console.log('2')
    })
})

Atau jauh lebih sederhana dan cara yang kurang mewah

function sleep(ms, f)
{
    return(
        setTimeout(f, ms)
    )
}


sleep(500, function()
{
    console.log('1')
    sleep(500, function()
    {
        console.log('2')
    })
})
console.log('Event chain launched')

Jika Anda hanya menunggu beberapa kondisi terjadi, Anda bisa menunggu seperti ini

function waitTill(condition, thenDo)
{
    if (eval(condition))
    {
        thenDo()
        return
    }

    setTimeout(
        ()    =>
        {
            waitTill(condition, thenDo)
        }
        ,
        1
    )
}

x=0

waitTill(
    'x>2 || x==1'
    ,
    ()    =>
    {
        console.log("Conditions met!")
    }
)

// Simulating the change
setTimeout(
    () =>
    {
        x = 1
    }
    ,
    1000
)

Íhor Mé
sumber
11

Pembaruan 2018

Safari, Firefox, dan Node.js terbaru sekarang juga mendukung async / menunggu / janji.

Menggunakan async / menunggu / Janji:

(Mulai 1/2017, didukung di Chrome, tetapi tidak di Safari, Internet Explorer, Firefox, Node.js)

'use strict';

function sleep(ms) {
  return new Promise(res => setTimeout(res, ms));
}

let myAsyncFunc = async function() {
  console.log('Sleeping');
  await sleep(3000);
  console.log('Done');
}

myAsyncFunc();

Pembaruan 2017

JavaScript telah berkembang sejak pertanyaan ini diajukan dan sekarang memiliki fungsi generator, dan async / await / Promise baru sedang diluncurkan. Di bawah ini ada dua solusi, satu dengan fungsi generator yang akan bekerja pada semua browser modern, dan yang lainnya, menggunakan async / menunggu baru yang belum didukung di mana-mana.

Menggunakan fungsi generator:

'use strict';

let myAsync = (g) => (...args) => {
    let f, res = () => f.next(),
        sleep = (ms) => setTimeout(res, ms);
    f = g.apply({sleep}, args); f.next();
};

let myAsyncFunc = myAsync(function*() {
    let {sleep} = this;
    console.log("Sleeping");
    yield sleep(3000);
    console.log("Done");
});

myAsyncFunc();

Perhatikan fakta bahwa kedua solusi ini asinkron. Ini berarti bahwa myAsyncFunc (dalam kedua kasus) akan kembali saat tidur.

Penting untuk dicatat bahwa pertanyaan ini berbeda dari Apa itu versi tidur JavaScript ()? di mana pemohon meminta tidur nyenyak (tidak ada eksekusi kode lain pada proses) daripada penundaan antara tindakan.

niry
sumber
1
Jawaban terbaik sejauh ini !! Saya telah menghabiskan 30 menit mencari di mana-mana untuk menemukan itu .. big thx !!!
538ROMEO
1
Saya melewatkan jawaban ini sambil mencari solusi dan menemukan kembali sepeda: D Kalau saja saya melihatnya sebelum itu akan menghemat berjam-jam !! Terpilih!
sserzant
let co = gen => (...args) => { let iter = gen(...args); let resume = () => new Promise((resolve, reject) => { let result = iter.next(); if (result.done) resolve(result.value); else Promise.resolve(result.value).then(resume).then(resolve, reject); }); return resume(); };akan membiarkan Anda let asyncAdd = co(function* (a, b) { console.log('Sleeping'); yield sleep(3000); console.log('Done'); return a + b; }); asyncAdd(3, 4).then(console.log);menggunakan definisi dari sleep()blok kode kedua Anda.
Patrick Roberts
3

Jika Anda ingin fungsi yang kurang kikuk daripada setTimeoutdan setInterval, Anda bisa membungkusnya dalam fungsi yang hanya membalik urutan argumen dan memberi mereka nama yang bagus:

function after(ms, fn){ setTimeout(fn, ms); }
function every(ms, fn){ setInterval(fn, ms); }

Versi CoffeeScript:

after = (ms, fn)-> setTimeout fn, ms
every = (ms, fn)-> setInterval fn, ms

Anda kemudian dapat menggunakannya dengan baik dengan fungsi anonim:

after(1000, function(){
    console.log("it's been a second");
    after(1000, function(){
        console.log("it's been another second");
    });
});

Sekarang terbaca dengan mudah sebagai "setelah N milidetik, ..." (atau "setiap N milidetik, ...")

1j01
sumber
2

Cara lain untuk melakukannya adalah dengan menggunakan Promise dan setTimeout (perhatikan bahwa Anda harus berada di dalam suatu fungsi dan atur sebagai asinkron dengan kata kunci async):

async yourAsynchronousFunction () {

    var a = 1+3;

    await new Promise( (resolve) => {
        setTimeout( () => { resolve(); }, 3000);
    }

    var b = a + 4;

}
Ostn
sumber
2

Berikut adalah cara yang sangat sederhana untuk melakukannya yang 'terasa' seperti tidur sinkron / jeda, tetapi adalah kode async yang sah.

// Create a simple pause function
const pause = (timeoutMsec) => new Promise(resolve => setTimeout(resolve,timeoutMsec))

async function main () {
    console.log('starting');
    // Call with await to pause.  Note that the main function is declared asyc
    await pause(3*1000)
    console.log('done');
}

pengguna2330237
sumber
1

Anda dapat menggunakan javascript biasa, ini akan memanggil fungsi / metode Anda setelah 5 detik:

setTimeout(()=> { your_function(); }, 5000);
Kuda Hitam
sumber
0

Ada beberapa cara untuk mengatasi masalah ini. Jika kita menggunakan setTimeoutfungsinya, mari kenali dulu. Fungsi ini memiliki tiga parameter: functionatau code, delay(dalam milidetik) dan parameters. Karena parameter fungsi atau kode diperlukan, yang lain opsional. Setelah Anda tidak memasukkan penundaan , itu akan diatur ke nol.

Untuk detail lebih lanjut tentang setTimeout() pergi ke tautan ini .

Versi sederhana:

var a = 1 + 3;
var b;
console.log('a = ' + a);
setTimeout(function(){ 
    b = a + 4; 
    console.log('b = ' + b);
}, 1000);

output:
a = 4
24 -> Number identifier dari daftar timeout aktif
b = 8


Menggunakan parameter pass:

var a = 1 + 3;
var b;
console.log('a = ' + a);
setTimeout(myFunction, 1000, a);

function myFunction(a)
{
    var b = a + 4;
    console.log('b = ' + b);
}

output:
a = 4
25 -> Number identifier dari daftar timeout aktif
b = 8



Dukungan Browser:

Chrome Firefox Edge Safari Opera
1.0 1.0 4.0 1.0 4.0
Alexandre Neukirchen
sumber
0

Ini adalah model saya yang menunjukkan bagaimana "tidur" atau "DoEvents" dalam javascript menggunakan fungsi generator (ES6). Kode yang dikomentari:

<html>
<head>
<script>
  "use strict"; // always
  // Based on post by www-0av-Com /programming/3143928
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
  var divelt, time0, globaln = 0; // global variables
  var MainGenObj = Main(); // generator object = generator function()
window.onload = function() {
  divelt = document.getElementsByTagName("body")[0]; // for addline()
  addline("typeof Main: " + typeof Main);
  addline("typeof MainDriver: " + typeof MainDriver);
  addline("typeof MainGenObj: " + typeof MainGenObj);
  time0 = new Date().valueOf(); // starting time ms
  MainDriver(); // do all parts of Main()
}
function* Main() { // this is "Main" -- generator function -- code goes here
  // could be loops, or inline, like this:

  addline("Part A, time: " + time() + ", " + ++globaln); // part A
  yield 2000;                    // yield for 2000 ms (like sleep)

  addline("Part B, time: " + time() + ", " +  ++globaln); // part B
  yield 3000;                    // yield for 3000 ms (or like DoEvents)

  addline("Part Z, time: " + time() + ", " +  ++globaln); // part Z (last part)
  addline("End, time: " + time());
}
function MainDriver() { // this does all parts, with delays
  var obj = MainGenObj.next(); // executes the next (or first) part of Main()
  if (obj.done == false) { // if "yield"ed, this will be false
    setTimeout(MainDriver, obj.value); // repeat after delay
  }
}
function time() { // seconds from time0 to 3 decimal places
  var ret = ((new Date().valueOf() - time0)/1000).toString();
  if (ret.indexOf(".") == -1) ret += ".000";
  while (ret.indexOf(".") >= ret.length-3) ret += "0";
  return ret;
}
function addline(what) { // output
  divelt.innerHTML += "<br />\n" + what;
}
</script>
</head>
<body>
<button onclick="alert('I\'m alive!');"> Hit me to see if I'm alive </button>
</body>
</html>
dcromley
sumber
0

Coba fungsi ini:

const delay = (ms, cb) => setTimeout(cb, ms)

Inilah cara Anda menggunakannya:

console.log("Waiting for 5 seconds.")
delay(5000, function() {
  console.log("Finished waiting for 5 seconds.")
})

Atau pergi gaya janji:

const delay = ms => new Promise(resolve => {
    setTimeout(resolve, ms)
})

Ini demo .

Richie Bendall
sumber