कैसे करें `setInterval` सिंक में अधिक व्यवहार करते हैं, या इसके बजाय` setTimeout` का उपयोग कैसे करें?


91

मैं एक संगीत कार्यक्रम पर काम कर रहा हूं जिसके लिए कई जावास्क्रिप्ट तत्वों को दूसरे के साथ सिंक करने की आवश्यकता होती है। मैं उपयोग कर रहा हूं setInterval, जो शुरू में वास्तव में अच्छी तरह से काम करता है। हालांकि, समय के साथ तत्व धीरे-धीरे सिंक से बाहर हो जाते हैं जो एक संगीत कार्यक्रम में खराब है।

मैंने ऑनलाइन पढ़ा है setTimeoutजो अधिक सटीक है, और आप setTimeoutकिसी तरह लूप कर सकते हैं । हालांकि, मुझे एक सामान्य संस्करण नहीं मिला है जो बताता है कि यह कैसे संभव है।

मूल रूप से मेरे कुछ कार्य हैं जैसे:

//drums
setInterval(function {
  //code for the drums playing goes here
}, 8000);

//chords
setInterval(function {
  //code for the chords playing goes here
}, 1000);

//bass
setInterval(function {
  //code for the bass playing goes here
}, 500);

यह शुरुआत में सुपर अच्छी तरह से काम करता है, लेकिन लगभग एक मिनट के दौरान, लगता है कि जब भी मैंने पढ़ा है, तब यह सिंक से बाहर हो जाता है setInterval। मैंने पढ़ा है कि setTimeoutलगातार अधिक सटीक हो सकता है।

किसी ने मुझे setTimeoutअनिश्चित काल के लिए पाश का उपयोग करने का एक मूल उदाहरण दिखा सकता है? वैकल्पिक रूप से, यदि setIntervalकिसी अन्य फ़ंक्शन के साथ या अधिक तुल्यकालिक परिणाम प्राप्त करने का एक तरीका है , तो कृपया मुझे बताएं।


2
आप कुछ कोड क्यों नहीं दिखाते हैं जो हमें दिखाते हैं कि आप क्या हासिल करना चाहते हैं और हम आपको बेहतर जवाब दे सकते हैं।
एंडी

1
मैंने ऑनलाइन पढ़ा है कि सेटटाइमआउट अधिक सटीक है : आपने कहां पढ़ा है? एक लिंक शामिल करें। मैं मान रहा हूं कि शायद यह setTimeoutआपके साथ एक मामला है जो यह गणना कर सकता है कि विलंब वास्तव में अगले टाइमआउट के लिए एक समायोजित समय था।
मैट बर्लैंड

2
किस बारे में requestAnimationFrame? आपको बस उस समय का संदर्भ देना होगा जब ऑडियो हर बार आपके requestAnimationFrameकॉलबैक पर चलता है।
जैस्पर

5
किसी भी प्रकार का टाइमर वास्तव में सटीक होने की गारंटी नहीं है। दिया गया मिलीसेकंड केवल न्यूनतम प्रतीक्षा समय है, लेकिन फ़ंक्शन को अभी भी बाद में बुलाया जा सकता है। यदि आप कई अंतरालों को समन्वित करने की कोशिश कर रहे हैं, तो अंतराल को नियंत्रित करने के बजाय, एक को समेकित करने का प्रयास करें।
जोनाथन लोनोस्की

1
यदि आप वास्तव में संगीत को किसी स्क्रीन पर सिंक करना चाहते हैं, तो आपको डोम को अपडेट करते समय ऑडियो के माध्यम से समय की प्रगति का संदर्भ देना होगा। अन्यथा चीजें ज्यादातर समय सिंक से बाहर हो जाएंगी।
जैस्पर

जवाबों:


181

आप setTimeoutपुनरावर्तन का उपयोग करके एक लूप बना सकते हैं :

function timeout() {
    setTimeout(function () {
        // Do Something Here
        // Then recall the parent function to
        // create a recursive loop.
        timeout();
    }, 1000);
}

के साथ समस्या यह setInterval()और setTimeout()कोई गारंटी नहीं है अपने कोड निर्दिष्ट समय में चलेंगे है कि वहाँ है। setTimeout()इसे पुनरावर्ती रूप से उपयोग और कॉल करके , आप यह सुनिश्चित कर रहे हैं कि कोडआउट का अगला पुनरावृत्ति शुरू होने से पहले टाइमआउट के अंदर के सभी पिछले ऑपरेशन पूरे हो जाएं।


1
इस विधि और उपयोग के बीच क्या अंतर है setInterval?
जैस्पर

4
इस दृष्टिकोण के साथ मुद्दा यह है कि यदि फ़ंक्शन को पूरा करने में 1000ms से अधिक समय लगता है, तो यह सभी पेट ऊपर जाता है। यह प्रत्येक 1000ms को निष्पादित करने की गारंटी नहीं है। setInterval है
TJC

@ टीजेसी: यह बहुत कुछ इस बात पर निर्भर करता है कि आप क्या हासिल करना चाहते हैं। यह अधिक महत्वपूर्ण हो सकता है कि पिछला कार्य अगले पुनरावृत्ति से पहले पूरा हो, या शायद नहीं।
मैट बर्लैंड

4
@TJC सही है, लेकिन यदि आपके पिछले ऑपरेशन setInterval()फिर से निष्पादित होने से पहले पूरे नहीं होते हैं , तो आपके चर और / या डेटा वास्तव में तेजी से सिंक से बाहर निकल सकते हैं। यदि मैं उदाहरण के लिए डेटा के लिए अजाक्सिंग कर रहा हूं, और सर्वर को जवाब देने में 1 सेकंड से अधिक समय लगता है, तो setInterval()मेरे पिछले डेटा का उपयोग करने से पहले मेरा अगला ऑपरेशन जारी रहने तक प्रसंस्करण समाप्त नहीं होगा। इस दृष्टिकोण के साथ, यह गारंटी नहीं है कि आपका फ़ंक्शन हर सेकंड बंद हो जाएगा। हालाँकि, यह गारंटी है कि अगले अंतराल से पहले ही आपके पिछले डेटा का प्रसंस्करण समाप्त हो जाएगा।
वार 10ck

1
@ War10ck, एक संगीत आधारित वातावरण दिया गया था, मैंने माना कि इसका उपयोग चर या अजाक्स कॉल को सेट करने के लिए नहीं किया जा सकता था जहां ऑर्डर मायने रखता था।
TJC

30

केवल पूरक के लिए। यदि आपको एक चर को पारित करने और इसे पुन: प्रसारित करने की आवश्यकता है, तो आप ऐसा कर सकते हैं:

function start(counter){
  if(counter < 10){
    setTimeout(function(){
      counter++;
      console.log(counter);
      start(counter);
    }, 1000);
  }
}
start(0);

आउटपुट:

1
2
3
...
9
10

प्रति सेकंड एक लाइन।


12

यह देखते हुए कि न तो समय बहुत सटीक हो रहा है, न केवल setTimeoutथोड़ा और सटीक होने का उपयोग करने का एक तरीका यह गणना करना है कि अंतिम पुनरावृत्ति के बाद कितनी देरी हुई, और फिर अगले पुनरावृत्ति को उचित रूप में समायोजित करें। उदाहरण के लिए:

var myDelay = 1000;
var thisDelay = 1000;
var start = Date.now();

function startTimer() {    
    setTimeout(function() {
        // your code here...
        // calculate the actual number of ms since last time
        var actual = Date.now() - start;
        // subtract any extra ms from the delay for the next cycle
        thisDelay = myDelay - (actual - myDelay);
        start = Date.now();
        // start the timer again
        startTimer();
    }, thisDelay);
}

तो पहली बार यह प्रतीक्षा करेगा (कम से कम) 1000 एमएस, जब आपका कोड निष्पादित हो जाता है, तो थोड़ी देर हो सकती है, 1046 एमएस कहें, इसलिए हम अगले चक्र के लिए हमारी देरी से 46 एमएस घटाते हैं और अगला विलंब होगा केवल 954 एमएस। यह टाइमर को देर से फायरिंग से रोक नहीं पाएगा (जिसकी उम्मीद की जा रही है), लेकिन आपको देरी को रोकने से रोकने में मदद करता है। (ध्यान दें: आप यह जांचना चाहते हैं कि thisDelay < 0किस कारण से देरी आपके लक्ष्य की देरी से दोगुनी है और आप एक चक्र से चूक गए - आप पर निर्भर करता है कि आप उस मामले को कैसे संभालना चाहते हैं।

बेशक, यह संभवतः आपको कई टाइमर को सिंक में रखने में मदद नहीं करेगा, जिस स्थिति में आप यह जानना चाहते हैं कि एक ही टाइमर के साथ उन सभी को कैसे नियंत्रित किया जाए।

इसलिए आपके कोड को देखते हुए, आपके सभी विलंब 500 के कई हैं, इसलिए आप ऐसा कुछ कर सकते हैं:

var myDelay = 500;
var thisDelay = 500;
var start = Date.now();
var beatCount = 0;

function startTimer() {    
    setTimeout(function() {
        beatCount++;
        // your code here...
        //code for the bass playing goes here  

        if (count%2 === 0) {
            //code for the chords playing goes here (every 1000 ms)
        }

        if (count%16) {
            //code for the drums playing goes here (every 8000 ms)
        }

        // calculate the actual number of ms since last time
        var actual = Date.now() - start;
        // subtract any extra ms from the delay for the next cycle
        thisDelay = myDelay - (actual - myDelay);
        start = Date.now();
        // start the timer again
        startTimer();
    }, thisDelay);
}

1
+1 नीट दृष्टिकोण। मैंने अगले रन के टाइमआउट की भरपाई के बारे में कभी नहीं सोचा। संभवत: आपको एक सटीक माप के करीब मिलेगा क्योंकि आप यह विचार कर सकते हैं कि जावास्क्रिप्ट बहु-थ्रेडेड नहीं है और लगातार समय अंतराल पर आग की गारंटी नहीं है।
वार 10। मार्क

यह भी खूब रही! मैं यह कोशिश करने जा रहा हूं और आपको बताता हूं कि यह कैसे होता है। मेरा कोड बड़े पैमाने पर है, इसलिए शायद इसमें थोड़ा समय लगेगा
user3084366

विचार करें: इस कोड को एक निश्चित (भले ही ऊपर के रूप में सही किया गया हो) पर ट्रिगर किया जाए, टाइम-लूप वास्तव में समस्या के बारे में सोचने का गलत तरीका हो सकता है। यह हो सकता है कि आपको वास्तव में प्रत्येक अंतराल की लंबाई "अगली बार-कुछ-के-कारण-से-होने वाली माइनस वर्तमान-समय" के लिए निर्धारित करनी चाहिए ।
मस्तम

आपने मेरे द्वारा लिखे गए समाधान का बेहतर संस्करण बनाया। मुझे यह पसंद है कि आपके चर नाम संगीत से भाषा का उपयोग करते हैं और आप जो कोशिश करते हैं, जो निर्माण करते हैं, उन्हें प्रबंधित करने की कोशिश करते हैं, जो मैं कोशिश भी नहीं करता।
मिगुएल वालेंसिया

8

ऑडियो टाइमिंग से निपटने का सबसे अच्छा तरीका वेब ऑडियो एपी के साथ है, इसमें एक अलग घड़ी है जो मुख्य धागे में हो रही परवाह किए बिना सटीक है। यहाँ क्रिस विल्सन की एक महान व्याख्या, उदाहरण, आदि है:

http://www.html5rocks.com/en/tutorials/audio/scheduling/

अधिक वेब ऑडियो एपीआई के लिए इस साइट के चारों ओर एक नज़र डालें, यह ठीक उसी तरह से विकसित किया गया था जैसे आप बाद में हैं।


मैं वास्तव में चाहता हूं कि और लोग इसे बढ़ाएं। setTimeoutअपर्याप्त से अत्यधिक ऑम्प्लेक्स के लिए सीमा का उपयोग करने वाले उत्तर । एक देशी फ़ंक्शन का उपयोग करना एक बेहतर विचार की तरह लगता है। यदि एपीआई आपके उद्देश्य की पूर्ति नहीं करता है, तो मैं एक विश्वसनीय तृतीय पक्ष शेड्यूलिंग लाइब्रेरी खोजने की कोशिश करूंगा।
तीन


3

अपनी आवश्यकता के अनुसार

बस मुझे कुछ समय के लिए सेटटाइमआउट का उपयोग करने का एक मूल उदाहरण दिखाएं

हमारे पास निम्नलिखित उदाहरण हैं जो आपकी सहायता कर सकते हैं

var itr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var  interval = 1000; //one second
itr.forEach((itr, index) => {

  setTimeout(() => {
    console.log(itr)
  }, index * interval)
})


1

मैं काम के जीवन में इस तरह का उपयोग करता हूं: इस मामले में "आम छोरों को भूल जाओ" और "setInterval" के इस संयोजन का उपयोग करें "setTimeOut" शामिल हैं:

    function iAsk(lvl){
        var i=0;
        var intr =setInterval(function(){ // start the loop 
            i++; // increment it
            if(i>lvl){ // check if the end round reached.
                clearInterval(intr);
                return;
            }
            setTimeout(function(){
                $(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
            },50);
            setTimeout(function(){
                 // do another bla bla bla after 100 millisecond.
                seq[i-1]=(Math.ceil(Math.random()*4)).toString();
                $("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
                $("#d"+seq[i-1]).prop("src",pGif);
                var d =document.getElementById('aud');
                d.play();                   
            },100);
            setTimeout(function(){
                // keep adding bla bla bla till you done :)
                $("#d"+seq[i-1]).prop("src",pPng);
            },900);
        },1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
    }

पुनश्च: समझें कि (setTimeOut) का वास्तविक व्यवहार: वे सभी एक ही समय में शुरू करेंगे "तीन ब्ला ब्ला ब्ला एक ही पल में नीचे गिनना शुरू कर देंगे" इसलिए निष्पादन की व्यवस्था करने के लिए एक अलग समयबाह्य करें।

पुनश्च 2: टाइमिंग लूप के लिए उदाहरण है, लेकिन एक प्रतिक्रिया लूप के लिए आप घटनाओं का उपयोग कर सकते हैं, async प्रतीक्षा का वादा कर सकते हैं ..


1

समाधान के साथ setTimeout लूप समस्या

// it will print 5 times 5.
for(var i=0;i<5;i++){
setTimeout(()=> 
console.log(i), 
2000)
}               // 5 5 5 5 5

// improved using let
for(let i=0;i<5;i++){
setTimeout(()=> 
console.log('improved using let: '+i), 
2000)
}

// improved using closure
for(var i=0;i<5;i++){
((x)=>{
setTimeout(()=> 
console.log('improved using closure: '+x), 
2000)
})(i);
} 


1
किसी भी विचार क्यों यह var और let के बीच भिन्न व्यवहार करता है?
Jay

1
@ जय ... उनके बीच अंतर यह है कि var फंक्स्ड स्कूप्ड है और ब्लॉक स्कॉप्ड है। अधिक स्पष्टीकरण के लिए आप medium.com/@josephcardillo/… के
वाहिद अख्तर

1

जैसा कि किसी और ने बताया, वेब ऑडियो एपीआई में एक बेहतर टाइमर है।

लेकिन सामान्य तौर पर, अगर ये घटनाएं लगातार होती हैं, तो आप कैसे इन सभी को एक ही टाइमर पर रख सकते हैं? मैं इस बारे में सोच रहा हूं कि एक चरण अनुक्रमक कैसे काम करता है।

व्यावहारिक रूप से, क्या यह ऐसा कुछ दिख सकता है?

var timer = 0;
var limit = 8000; // 8000 will be the point at which the loop repeats

var drumInterval = 8000;
var chordInterval = 1000;
var bassInterval = 500;

setInterval(function {
    timer += 500;

    if (timer == drumInterval) {
        // Do drum stuff
    }

    if (timer == chordInterval) {
        // Do chord stuff
    }

    if (timer == bassInterval) {
        // Do bass stuff
    }

    // Reset timer once it reaches limit
    if (timer == limit) {
        timer = 0;
    }

}, 500); // Set the timer to the smallest common denominator

0

function appendTaskOnStack(task, ms, loop) {
    window.nextTaskAfter = (window.nextTaskAfter || 0) + ms;

    if (!loop) {
        setTimeout(function() {
            appendTaskOnStack(task, ms, true);
        }, window.nextTaskAfter);
    } 
    else {
        if (task) 
            task.apply(Array(arguments).slice(3,));
        window.nextTaskAfter = 0;
    }
}

for (var n=0; n < 10; n++) {
    appendTaskOnStack(function(){
        console.log(n)
    }, 100);
}


1
आपके समाधान की एक व्याख्या बहुत सराहना की जाएगी!
टन

-2

मुझे लगता है कि फ़ंक्शन के अंत में टाइमआउट करना बेहतर है।

function main(){
    var something; 
    make=function(walkNr){
         if(walkNr===0){
           // var something for this step      
           // do something
         }
         else if(walkNr===1){
           // var something for that step 
           // do something different
         }

         // ***
         // finally
         else if(walkNr===10){
           return something;
         }
         // show progress if you like
         setTimeout(funkion(){make(walkNr)},15,walkNr++);  
   }
return make(0);
}   

यह तीन कार्य आवश्यक हैं क्योंकि दूसरे फ़ंक्शन में vars को हर बार डिफ़ॉल्ट मान से अधिलेखित किया जाएगा। जब प्रोग्राम पॉइंटर सेटटाइमआउट तक पहुंचता है तो एक कदम पहले ही गणना की जाती है। फिर बस स्क्रीन को थोड़ा समय चाहिए।


-2

कोड में var के बजाय let का उपयोग करें:

for(let i=1;i<=5;i++){setTimeout(()=>{console.log(i)},1000);}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.