Как создать асинхронную функцию в Javascript?

Я имею в виду, проверить это код :

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

как вы можете видеть в консоли, функция "animate "является асинхронной, и она" развивает " поток блочного кода обработчика событий. На самом деле :

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

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

следуйте потоку блочного кода!

если я хочу создать свою function asyncFunct() { } С этим поведением, как я могу сделать это с javascript / jquery? Я думаю, что есть стратегия без использования setTimeout()

12 ответов


вы не можете создать действительно настраиваемую асинхронную функцию. В конечном итоге вам придется использовать технологию, предоставленную изначально, например:

  • setInterval
  • setTimeout
  • requestAnimationFrame
  • XMLHttpRequest
  • WebSocket
  • Worker
  • некоторые API HTML5, такие как API файлов, API веб-базы данных
  • технологий, которые поддерживают onload
  • ... много другие!--12-->

на самом деле, для анимации jQuery использует setInterval.


вы можете использовать таймер:

setTimeout( yourFn, 0 );

(где yourFn является ссылкой на вашу функцию)

или Лодашь:

_.defer( yourFn );

откладывает вызов func пока текущий стек вызовов не очистится. Любые дополнительные аргументы предоставляются func, когда он вызывается.


здесь у вас есть простое решение (другие пишут об этом) http://www.benlesh.com/2012/05/calling-javascript-function.html

и здесь у вас есть выше готовое решение:

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

вот функция, которая принимает другую функцию и выводит версию, которая работает асинхронно.

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

Он используется как простой способ сделать асинхронной функции:

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

это отличается от ответа @fider, потому что сама функция имеет свою собственную структуру (без обратного вызова, она уже в функции), а также потому, что она создает новую функцию, которую можно использовать.


на этой странице проведет вас через основы создания асинхронной функции JavaScript.

выполнение асинхронных вызовов функций в JavaScript с использованием аргументов обычно включает в построении аргументации выражение в setTimeout или setInterval вручную.

Если это не решит его для вас, ознакомьтесь с документацией на .


Edit: Я не понял вопрос. В браузере я бы использовал setTimeout. Если бы было важно, чтобы он работал в другом потоке, я бы использовал Веб-Работников.


Если вы хотите использовать параметры и регулировать максимальное количество асинхронных функций, вы можете использовать простой асинхронный рабочий, который я построил:

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

поздно, но показать простое решение с помощью promises после введения их в ЕС6, он обрабатывает асинхронные вызовы намного проще:

вы устанавливаете асинхронный код в новом обещании:

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

Примечание Для установки resolve() когда асинхронный вызов заканчивается.
Затем вы добавляете код, который хотите запустить после завершения асинхронного вызова внутри .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


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 Кажется мне подходящим.


рядом с отличным ответом @pimvdb, и на всякий случай вам где интересно,асинхронность.js также не предлагает действительно асинхронных функций. Вот (очень) урезанная версия основного метода библиотеки:

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.


к сожалению, JavaScript не предоставляет асинхронную функциональность. Он работает только в одном потоке. Но большинство современных браузеров предоставляют Workers, это вторые скрипты, которые выполняются в фоновом режиме и могут возвращать результат. Итак, я пришел к решению, я думаю, что полезно асинхронно запускать функцию, которая создает работника для каждого асинхронного вызова.

код ниже содержит функцию 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 С огромным числом, чтобы занять время как демонстрация асинхронности, а затем получает двойник переданного числа. The async метод обеспечивает function который вызывает нужную функцию в фоновом режиме, и в том, что предоставляется в качестве параметра async обратные вызовы в return в своем уникальном параметре. Так в функция обратного вызова I alert результат.


MDN имеет хороший пример об использовании setTimeout, сохраняющего "это".

следующим образом:

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

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