2012-03-01 5 views
110

Я имею в виду, проверить его это code:Как создать асинхронную функцию в 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");  
}); 

, как вы можете видеть в консоли, функция «одушевленные» является асинхронной, и «вилка» поток блочного кода обработчика событий. Фактически:

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

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

следует за потоком блок-кода!

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

+0

посмотрите на источники jQuery :) – yatskevich

+0

. .animate() mathod использует обратный вызов. Анимация вызовет обратный вызов, когда анимация будет завершена. Если вам нужно то же поведение .animate(), что вам нужно, это обратный вызов (вызываемый функцией «main» после некоторых других операций). Другое дело, если вам нужна «полная» асинхронная функция (функция, называемая с блокировкой потока выполнения). В этом случае вы можете использовать setTimeout() с задержкой около 0. –

+0

@Fabio Buda: почему callback() должен реализовывать своего рода асинхронное? На самом деле, это не http://jsfiddle.net/5H9XT/9/ – markzzz

ответ

148

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

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

В самом деле, для анимации JQuery usessetInterval.

+3

Также добавьте веб-работников в этот список ... –

+1

Я был обсуждая это с другом вчера, чтобы этот ответ был совершенным! Я понимаю и могу идентифицировать асинхронные функции и правильно использовать их в JS. Но просто * почему * мы не можем реализовать пользовательские, мне не ясно. Это как черный ящик, который мы знаем, как заставить его работать (используя, скажем, 'setInterval'), но мы даже не можем его открыть, чтобы посмотреть, как это делается. У вас больше информации по этому вопросу? –

+2

@MatheusFelipe Эти функции являются родными для реализации javascript-движка, и единственное, на что вы можете положиться, - это спецификации, например. HTML5 таймеры] (http://www.w3.org/TR/2011/WD-html5-20110525/timers.html) и доверять природе черного ящика, что они ведут себя в соответствии со спецификациями. – Spoike

3

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

+1

? Это не делает асинхронную функцию: O – markzzz

+0

http://jsfiddle.net/5H9XT/9/ из-за вашего ответа, это не должно работать ... – markzzz

53

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

setTimeout(yourFn, 0); 

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

или с underscore.js:

_.defer(yourFn); 

постановляет отложить вызов функции до текущий стек вызовов очищен, похож на использование setTimeout с del ay of 0. Полезно для выполнения дорогостоящих вычислений или рендеринга HTML в кусках без блокировки потока пользовательского интерфейса от обновления.

+1

Это не работает, моя функция javascript, которая рисует на холсте, сохраняется в результате чего пользовательский интерфейс не отвечает. – gab06

+1

@ gab06 - Я бы сказал, что ваша функция рисования холста блокирует свои собственные веские причины. Разделите его действие на многие более мелкие и вызовите каждый из них таймером: вы увидите, что интерфейс таким образом реагирует на щелчки мыши и т. Д. –

5

This page Просматривает основы создания функции async javascript.

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

Если это не поможет вам, ознакомьтесь с документацией по функции animate.

22

здесь у вас есть простое решение (другие пишут об этом) 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 х 2 3' или ' 1 2 х 3' или '1 2 3 х'):

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

ТЕСТ 2 (будет аль способы вывода «х 1»):

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

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

+5

TEST 1 может фактически выводить только «1 2 3 x», и TEST 2 гарантированно выводит «1 x» каждый раз. Причина неожиданных результатов в TEST 2 заключается в том, что вызывается 'console.log (1)', а вывод ('undefined') передается как 2-й аргумент' async() '. В случае TEST 1, я думаю, вы не полностью понимаете очередь выполнения JavaScript. Поскольку каждый из вызовов 'console.log()' происходит в одном стеке, '' 'гарантированно будет регистрироваться последним. Я проголосовал за этот ответ за дезинформацию, но не имею достаточного количества репутации. –

+1

@Joshua: Кажется, что @fider должен был написать TEST 2 как: 'async (function() {console.log ('x')}, function() {console.log (1)});'. – nzn

+0

Да @nzn и @Joshua Я имел в виду 'TEST 2 as: async (function() {console.log ('x')}, function() {console.log (1)});' - я уже исправил его – fider

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

Хотя принципиально не отличается от других решений, я думаю, что мое решение делает несколько дополнительных приятных вещей:

  • позволяет параметров к функциям
  • она проходит выходной функции обратного вызова к
  • он добавляется к Function.prototype позволяя лучше способ назвать это

Кроме того, сходство с встроенной функцией Function.prototype.apply представляется целесообразным для меня.

0

MDN имеет good example об использовании setTimeout, сохраняя это «это».

Как следующее:

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

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

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

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

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

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

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

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

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

+0

setTimeout не может использоваться в цикле * (вызывать одну и ту же функцию несколько раз с различными аргументами) *. – user2284570

+0

@ user2284570 Вот для чего закрыты. '(function (a) {asyncFunction (a);}) (a)' – Swivel

+0

IIRC, вы также можете достичь этого без закрытия: 'setTimeout (asyncFunction, 0, a);' – Swivel

3

Я вижу здесь, что никто не говорил о обещании. Поэтому я рекомендую вам узнать о обещании: see this link

1

Рядом с отличным ответом от @pimvdb, и на всякий случай, когда вы задаетесь вопросом, async.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.

1

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

0

Поздно, но, чтобы показать простое решение с использованием 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");    \t 
 
      resolve(); 
 
     }); \t \t \t 
 
    }); 
 
    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

Смежные вопросы