web-dev-qa-db-ja.com

Javascriptで非同期関数を作成するにはどうすればよいですか?

これを確認してください code

<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");    
});

コンソールで見ることができるように、「アニメーション」関数は非同期であり、イベントハンドラーブロックコードのフローを「分岐」します。実際には :

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

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

ブロックコードの流れに従ってください!

この動作でfunction asyncFunct() { }を作成したい場合、javascript/jqueryでどのようにできますか? setTimeout()を使用しない戦略があると思います

131
markzzz

本当にカスタムの非同期関数を作成することはできません。最終的には、次のようなネイティブで提供されるテクノロジーを活用する必要があります。

  • setInterval
  • setTimeout
  • requestAnimationFrame
  • XMLHttpRequest
  • WebSocket
  • Worker
  • ファイルAPI、WebデータベースAPIなどの一部のHTML5 API
  • onloadをサポートするテクノロジー
  • ...他の多くの

実際、アニメーションjQueryでは sessetInterval

174
pimvdb

タイマーを使用できます:

setTimeout( yourFn, 0 );

yourFnは関数への参照です)

または、 Lodash で:

_.defer( yourFn );

現在の呼び出しスタックがクリアされるまでfuncの呼び出しを延期します。 funcが呼び出されると、追加の引数が提供されます。

64
Šime Vidas

ここに簡単な解決策があります(他のそれについて書いてください) http://www.benlesh.com/2012/05/calling-javascript-function.html

そして、ここで上記の準備ができたソリューションがあります:

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

テスト1(は「1 x 2 3」または「1 2 x 3」または「1 2 3 x」を出力する場合があります):

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

テスト2(は常に 'x 1'を出力します):

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

この関数はタイムアウト0で実行されます-非同期タスクをシミュレートします

28
fider

別の関数を取り込んで、非同期を実行するバージョンを出力する関数を次に示します。

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

非同期関数を作成する簡単な方法として使用されます。

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

これは@fiderの答えとは異なります。関数自体に独自の構造があり(コールバックが追加されておらず、既に関数に含まれている)、また使用可能な新しい関数が作成されるためです。

9
Ethan McTague

このページ 非同期javascript関数の作成の基本を説明します。

ES2017以降、非同期javacript関数は非常に簡単に記述できます。 Promises の詳細もお読みください。

5
shanabus

編集:私は質問を完全に誤解しました。ブラウザでは、setTimeoutを使用します。別のスレッドで実行することが重要な場合は、 Web Workers を使用します。

遅いですが、 ES6 で紹介した後にpromisesを使用して簡単な解決策を示すために、非同期呼び出しをはるかに簡単に処理します。

新しいpromiseに非同期コードを設定します。

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

非同期呼び出しが終了したときにresolve()を設定することに注意してください。
次に、promiseの.then()内で非同期呼び出しが終了した後に実行するコードを追加します。

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

以下がそのスニペットです。

$('#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>

または JSFiddle

3
hd84335

パラメーターを使用し、非同期関数の最大数を調整する場合は、私が作成した単純な非同期ワーカーを使用できます。

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);
    }
}

次のように使用できます。

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

var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);
3
João Alves
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);

他のソリューションと根本的に違いはありませんが、私のソリューションはさらにいくつかの素晴らしいことをしていると思います。

  • 関数へのパラメーターを許可します
  • 関数の出力をコールバックに渡します
  • それはFunction.prototypeに追加され、より良い方法で呼び出すことができます

また、組み込み関数Function.prototype.applyとの類似性は私にとって適切なようです。

2

@pimvdbの素晴らしい答えの隣に、念のため、 async.js でも真の非同期関数を提供していません。以下は、ライブラリのmainメソッドの(非常に)簡略化されたバージョンです。

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);
    };
}

したがって、この関数はエラーから保護し、コールバックに適切に処理して処理しますが、コードは他のJS関数と同じように同期しています。

1
Mike M

残念ながら、JavaScriptは非同期機能を提供していません。単一の1つのスレッドでのみ機能します。しかし、最新のブラウザのほとんどはWorkersを提供します。これは、バックグラウンドで実行され、結果を返すことができる2番目のスクリプトです。だから、非同期呼び出しごとにワーカーを作成する関数を非同期的に実行すると便利だと思うソリューションに到達しました。

以下のコードには関数が含まれています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);
};

これは使用例:

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

これは、forを巨大な数で反復して非同期性のデモンストレーションとして時間をかける関数を実行し、渡された数の2倍を取得します。 asyncメソッドは、必要な関数をバックグラウンドで呼び出すfunctionを提供し、asyncのパラメーターとして提供されるメソッドでは、その固有のパラメーターでreturnをコールバックします。したがって、コールバック関数では、結果をalertにします。

1
Davide Cannizzo

MDNには、「this」を保持するsetTimeoutの使用に関する 良い例 があります。

次のように:

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

$(".someSelector").each(function() {
    setTimeout(doSomething.bind(this), 0);
});
0
xtian