Javascript'te nasıl Eşzamansız bir işlev oluşturabilirim?


143

Bu kodu inceleyin :

<a href="#" id="link">Link</a>
<span>Moving</span>

$('#link').click(function () {
    console.log("Enter");
    $('#link').animate({ width: 200 }, 2000, function() {
         console.log("finished");            
    });    
    console.log("Exit");    
});

Konsolda görebileceğiniz gibi, "canlandır" işlevi eşzamansızdır ve olay işleyici blok kodunun akışı "çatal" dır. Aslında :

$('#link').click(function () {
    console.log("Enter");
    asyncFunct();
    console.log("Exit");    
});

function asyncFunct() {
    console.log("finished");
}

blok kod akışını takip edin!

Benim function asyncFunct() { }bu davranış ile oluşturmak istiyorsanız , javascript / jquery ile nasıl yapabilirim? Ben kullanılmadan bir strateji olduğunu düşünüyorum setTimeout()


jQuery kaynaklarına bir göz atın :)
yatskevich

.Animate () mathod bir geri çağrı kullanır. Animate, animasyon tamamlandığında geri aramayı çağırır. .Animate () ile aynı davranışa ihtiyacınız varsa, ihtiyacınız olan geri arama (diğer bazı işlemlerden sonra "ana" işlev tarafından çağrılır). "Dolu" bir eşzamansız işleve (yürütme akışını engelleyen bir işlev) ihtiyacınız varsa farklıdır. Bu durumda, 0'a yakın bir gecikmeyle setTimeout () kullanabilirsiniz.
Fabio Buda

@Fabio Buda: Geri arama () neden bir tür zaman uyumsuzluğu gerçekleştirmeli? Aslında, jsfiddle.net/5H9XT/9
markzzz

Aslında "geri arama" sonra setTimeout ile bir "tam" zaman uyumsuz yöntemi gösterdi. Ben fonksiyon diğer kod sonra çağrılır şekilde sözde asenkron olarak geri çağırmak demek :-)
Fabio Buda

Yanıtlar:


185

Gerçekten özel bir asenkron işlev yapamazsınız. Sonunda aşağıdakiler gibi yerel olarak sağlanan bir teknolojiden yararlanmanız gerekir:

  • setInterval
  • setTimeout
  • requestAnimationFrame
  • XMLHttpRequest
  • WebSocket
  • Worker
  • Dosya API'sı, Web Veritabanı API'sı gibi bazı HTML5 API'leri
  • Destekleyen teknolojiler onload
  • ... diğerleri

Aslında, jQuery animasyon için kullanır setInterval .


1
Bunu dün bir arkadaşımla tartışıyordum, bu yüzden bu cevap mükemmel! Ben zaman uyumsuz fonksiyonları anlamak ve tanımlamak ve JS düzgün kullanmak. Ama sadece özel olanları neden uygulayamıyorum bana açık değil. Nasıl çalıştığını bildiğimiz bir kara kutu gibi (kullanarak, söyleyin, setInterval) ama nasıl yapıldığını görmek için bile açamıyoruz. Konu hakkında daha fazla bilginiz var mı?
Matheus Felipe

2
@MatheusFelipe, javascript motorunun uygulamasına özgüdür ve güvenebileceğiniz tek şey, örneğin HTML5 zamanlayıcılarıdır ve özelliklere göre davrandıkları kara kutu doğasına güvenir.
Spoike

10
@MatheusFelipe youtu.be/8aGhZQkoFbQ şimdiye kadar bu konuyla ilgili en iyi konuşma ...
Andreas Niedermair

Bazı uygulamalar, özellikle Node.js, desteksetImmediate
Jon Surrell

ne hakkında promises. Verir awaitablemi?
Nithin Chandran

69

Bir zamanlayıcı kullanabilirsiniz:

setTimeout( yourFn, 0 );

(nerede yourFn işlevinize bir referans )

veya Lodash ile :

_.defer( yourFn );

funcGeçerli çağrı yığını temizlenene kadar çağrıyı defers . Herhangi bir ek argüman funcçağrıldığında verilir.


3
Bu işe yaramaz, bir tuvalde çizilen javascript işlevim, kullanıcı arayüzünün yanıt vermemeye devam etmesini sağlar.
gab06

2
@ gab06 - Tuval çizim işlevinizin kendi iyi nedenlerinden dolayı engellendiğini söyleyebilirim. Eylemini daha küçük olanlara ayırın ve her birini bir zamanlayıcı ile çağırın: arayüzün bu şekilde fare tıklamalarınıza vb.
Tepki

Bağlantı koptu.
JulianSoto

1
@JulianSoto düzeltildi
Šime Vidas

1
setTimeoutHTML5 özelliklerine göre minimum süre 4 milisaniyedir. 0 vermek yine de bu minimum süreyi alacaktır. Ama evet, bir fonksiyon erteleyici olarak iyi çalışıyor.
hegez

30

burada basit bir çözümünüz var (diğer yazı hakkında) http://www.benlesh.com/2012/05/calling-javascript-function.html

Ve burada hazır çözümünüz var:

function async(your_function, callback) {
    setTimeout(function() {
        your_function();
        if (callback) {callback();}
    }, 0);
}

TEST 1 ( '1 x 2 3' veya '1 2 x 3' veya '1 2 3 x' çıktı alabilir ):

console.log(1);
async(function() {console.log('x')}, null);
console.log(2);
console.log(3);

TEST 2 ( her zaman 'x 1' çıktısı verilir ):

async(function() {console.log('x');}, function() {console.log(1);});

Bu işlev zaman aşımı 0 ile yürütülür - zaman uyumsuz görevi simüle eder


6
TEST 1 aslında sadece '1 2 3 x' çıktısı verir ve TEST 2'nin her seferinde '1 x' çıkışı garanti edilir. TEST 2'deki beklenmedik sonuçların nedeni console.log(1)çağrılması ve output ( undefined) işlevinin 2. argüman olarak iletilmesidir async(). TEST 1 örneğinde, JavaScript'in yürütme sırasını tam olarak anlamadığınızı düşünüyorum. Çünkü çağrıların her birinin console.log()aynı yığın içinde gerçekleşmesi nedeniyle, en xson günlüğe kaydedilmesi garanti edilir. Yanlış bilgilendirme için bu cevabı aşağı oylardım ama yeterli temsilcim yok.
Joshua Piccari

1
@Joshua: Bu @fider olarak yazma TEST 2 anlamına görünüyor: async(function() {console.log('x')}, function(){console.log(1)});.
nzn

Evet @nzn ve @Joshua Demek istediğim TEST 2 as: async(function() {console.log('x')}, function(){console.log(1)});- zaten düzelttim
fider

TEST 2 çıkışı zaman uyumsuz (işlev () {setTimeout (() => {console.log ('x');}, 1000)}, function () {console.log (1);});
Mohsen

10

İşte başka bir işlevi alan ve zaman uyumsuz çalışan bir sürüm çıktısı bir işlev.

var async = function (func) {
  return function () {
    var args = arguments;
    setTimeout(function () {
      func.apply(this, args);
    }, 0);
  };
};

Zaman uyumsuz bir işlev yapmanın basit bir yolu olarak kullanılır:

var anyncFunction = async(function (callback) {
    doSomething();
    callback();
});

Bu, @ fider'ın cevabından farklıdır, çünkü işlevin kendi yapısı vardır (geri çağrı eklenmez, zaten fonksiyondadır) ve ayrıca kullanılabilecek yeni bir işlev oluşturur.


setTimeout bir döngüde kullanılamaz (aynı işlevi farklı bağımsız değişkenlerle birkaç kez çağırın) .
user2284570

@ user2284570 Kapaklar bunun için. (function(a){ asyncFunction(a); })(a)
döner

1
IIRC, bunu kapanmadan da başarabilirsiniz:setTimeout(asyncFunction, 0, a);
döner

1
Async ile kastettiğimizde: ana iş parçacığına paralel olarak arka planda çalışıyorsa, bu gerçekten asenkron değildir. Tüm yapmanız gereken işlemi process.nextTick için geciktirmektir. İşlevdeki kodunuz ne olursa olsun ana iş parçacığında yürütülür. İşlev PI hesaplamak için ayarlanmışsa, uygulama zaman aşımı ile veya zaman aşımı olmadan donacaktır!
Mike M

1
Bu cevabın neden kaldırıldığını anlamıyorum. Benim kodunda bu koyduğunuzda, fonksiyonu kadar program blokları gerektiğini tam olarak ne olduğu, bitti değil yapmak.
Martin Argerami

6

Edit: Soruyu tamamen yanlış anladım. Tarayıcıda kullanırdım setTimeout. Başka bir iş parçacığında koştu önemli olsaydı, Web İşçileri kullanırdım .


1
? Bu bir zaman uyumsuz işlevi
yapmaz

6

Geç oldu, ancak ES6'dapromises tanıtıldıktan sonra kolay bir çözüm göstermek için senkronize olmayan çağrıları çok daha kolay hallediyor :

Eşzamansız kodu yeni bir vaatte ayarlıyorsunuz:

var asyncFunct = new Promise(function(resolve, reject) {
    $('#link').animate({ width: 200 }, 2000, function() {
        console.log("finished");                
        resolve();
    });             
});

resolve()Zaman uyumsuz çağrı bittiğinde ayarlanacağını unutmayın .
Sonra zaman uyumsuz çağrı bittikten sonra çalıştırmak istediğiniz kodu .then()sözün içine ekleyin :

asyncFunct.then((result) => {
    console.log("Exit");    
});

İşte bir pasajı:

$('#link').click(function () {
    console.log("Enter");
    var asyncFunct = new Promise(function(resolve, reject) {
        $('#link').animate({ width: 200 }, 2000, function() {
            console.log("finished");            	
            resolve();
        }); 			
    });
    asyncFunct.then((result) => {
        console.log("Exit");    
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#" id="link">Link</a>
<span>Moving</span>

veya JSFiddle


6

Bu sayfa zaman uyumsuz javascript işlevi oluşturmanın temelleri konusunda size yol gösterir.

ES2017'den bu yana, asenkron javacript işlevlerinin yazılması çok daha kolaydır. Ayrıca Promises hakkında daha fazla bilgi almalısınız .


Bağlantı öldü.
Martin Argerami

3

Parametreleri kullanmak ve maksimum eşzamansız işlev sayısını düzenlemek istiyorsanız, oluşturduğum basit bir eşzamansız işçiyi kullanabilirsiniz:

var BackgroundWorker = function(maxTasks) {
    this.maxTasks = maxTasks || 100;
    this.runningTasks = 0;
    this.taskQueue = [];
};

/* runs an async task */
BackgroundWorker.prototype.runTask = function(task, delay, params) {
    var self = this;
    if(self.runningTasks >= self.maxTasks) {
        self.taskQueue.push({ task: task, delay: delay, params: params});
    } else {
        self.runningTasks += 1;
        var runnable = function(params) {
            try {
                task(params);
            } catch(err) {
                console.log(err);
            }
            self.taskCompleted();
        }
        // this approach uses current standards:
        setTimeout(runnable, delay, params);
    }
}

BackgroundWorker.prototype.taskCompleted = function() {
    this.runningTasks -= 1;

    // are any tasks waiting in queue?
    if(this.taskQueue.length > 0) {
        // it seems so! let's run it x)
        var taskInfo = this.taskQueue.splice(0, 1)[0];
        this.runTask(taskInfo.task, taskInfo.delay, taskInfo.params);
    }
}

Bu şekilde kullanabilirsiniz:

var myFunction = function() {
 ...
}
var myFunctionB = function() {
 ...
}
var myParams = { name: "John" };

var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);

2
Function.prototype.applyAsync = function(params, cb){
      var function_context = this;
      setTimeout(function(){
          var val = function_context.apply(undefined, params); 
          if(cb) cb(val);
      }, 0);
}

// usage
var double = function(n){return 2*n;};
var display = function(){console.log(arguments); return undefined;};
double.applyAsync([3], display);

Temel olarak diğer çözümlerden farklı olmasa da, çözümümün birkaç ek güzel şey yaptığını düşünüyorum:

  • fonksiyonların parametrelerine izin verir
  • işlevin çıktısını geri aramaya geçirir
  • onu Function.prototypedaha güzel bir şekilde aramaya izin vermek için eklendi

Ayrıca, yerleşik işleve benzerlik Function.prototype.applybenim için uygun görünüyor.


1

@Pimvdb tarafından verilen büyük cevabın yanında, merak ediyorsanız, async.js de gerçekten asenkron işlevler sunmuyor. İşte kütüphanenin ana yönteminin (çok) soyulmuş bir versiyonu:

function asyncify(func) { // signature: func(array)
    return function (array, callback) {
        var result;
        try {
            result = func.apply(this, array);
        } catch (e) {
            return callback(e);
        }
        /* code ommited in case func returns a promise */
        callback(null, result);
    };
}

Böylece işlev hatalardan korur ve işlemek için geri aramaya incelikle verir, ancak kod diğer JS işlevleri kadar senkronizedir.


1

Ne yazık ki, JavaScript zaman uyumsuz bir işlev sağlamaz. Sadece tek bir iş parçacığında çalışır. Ancak modern tarayıcıların çoğu, arka planda yürütülen ve bir sonuç döndürebilen ikinci komut dosyaları olan Workers sağlar . Böylece, her zaman uyumsuz çağrı için bir işçi yaratan bir işlevi zaman uyumsuz olarak çalıştırmanın yararlı olduğunu düşünüyorum bir çözüme ulaştım.

Aşağıdaki kod , arka planda çağrılacak işlevi içerir async .

Function.prototype.async = function(callback) {
    let blob = new Blob([ "self.addEventListener('message', function(e) { self.postMessage({ result: (" + this + ").apply(null, e.data) }); }, false);" ], { type: "text/javascript" });
    let worker = new Worker(window.URL.createObjectURL(blob));
    worker.addEventListener("message", function(e) {
        this(e.data.result);
    }.bind(callback), false);
    return function() {
        this.postMessage(Array.from(arguments));
    }.bind(worker);
};

Bu kullanım için bir örnektir:

(function(x) {
    for (let i = 0; i < 999999999; i++) {}
        return x * 2;
}).async(function(result) {
    alert(result);
})(10);

Bu for, eşzamansızlığın bir gösterimi olarak zaman almak için çok sayıda yineleyen bir işlevi yürütür ve sonra geçen sayının iki katını alır. asyncYöntem sağlar function, arka planda istenen işlevini çağırır ve bu burada parametresi olarak sağlanan asyncgeri aramalar returneşsiz parametresinde. Yani geri arama işlevinde alertsonuç.


0

MDN'nin setTimeout kullanımı konusunda "this" korunmasına iyi bir örneği vardır .

Aşağıdaki gibi:

function doSomething() {
    // use 'this' to handle the selected element here
}

$(".someSelector").each(function() {
    setTimeout(doSomething.bind(this), 0);
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.