Как я могу создать асинхронную функцию в Javascript? - PullRequest
132 голосов
/ 01 марта 2012

Проверьте этот код :

<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 ]

174 голосов
/ 01 марта 2012

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

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

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

64 голосов
/ 01 марта 2012

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

setTimeout( yourFn, 0 );

(где yourFn - ссылка на вашу функцию)

или, с Лодаш :

_.defer( yourFn );

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

28 голосов
/ 28 июня 2013

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

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

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

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

TEST 2 ( всегда будет выводить 'x 1' ):

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

Эта функция выполняется с таймаутом 0 - она ​​будет имитировать асинхронную задачу

9 голосов
/ 14 ноября 2015

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

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

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

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

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

5 голосов
/ 01 марта 2012

Эта страница познакомит вас с основами создания асинхронной функции JavaScript.

Начиная с ES2017, асинхронные функции javacript намного легче писать. Вы также должны прочитать больше на Обещания .

4 голосов
/ 01 марта 2012

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

3 голосов
/ 30 декабря 2017

Поздно, но чтобы показать простое решение с использованием promises после их введения в ES6 , он намного легче обрабатывает асинхронные вызовы:

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

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

3 голосов
/ 12 ноября 2015

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

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);
2 голосов
/ 03 августа 2015
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 кажется мне подходящим.

1 голос
/ 12 ноября 2017

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...