2012-03-01 91 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 2012-03-01 13:19:14

+0

.animate()方法使用回調。動畫完成後,Animate將調用回調。如果你需要和.animate()相同的行爲,你需要的是一個回調(在其他操作之後由「main」函數調用)。如果你需要一個「完整的」異步函數(這是一個在不阻止執行流程的情況下調用的函數),它是不同的。在這種情況下,您可以使用setTimeout(),延遲時間接近0。 – 2012-03-01 13:23:25

+0

@Fabio Buda:爲什麼callback()應該實現一種異步?事實上,它實際上並沒有http://jsfiddle.net/5H9XT/9/ – markzzz 2012-03-01 13:45:05

回答

148

您無法制作真正自定義的異步功能。最後,您必須利用上本地提供的技術,如:

  • setInterval
  • setTimeout
  • ​​
  • XMLHttpRequest
  • WebSocket
  • Worker
  • 一些HTML5 API,例如作爲File API,Web Databas支持onload
  • ËAPI
  • 技術......許多人

事實上,對於動畫jQuery的usessetInterval

+3

另外,將web工作人員添加到該列表中... – 2012-03-01 13:31:51

+1

我是與朋友昨天討論這個問題,所以這個答案是完美的!我理解並可以識別異步函數並在JS中正確使用它們。但簡單地說*爲什麼我們不能實現定製的功能對我來說並不清楚。它就像一個黑盒子,我們知道如何使它工作(使用,比如'setInterval'),但我們甚至無法打開它看它是如何完成的。你碰巧有關於這個問題的更多信息? – 2015-03-27 22:09:21

+2

@MatheusFelipe這些函數是javascript引擎實現的原生代碼,唯一可以依賴的是規範,例如[ HTML5計時器](http://www.w3.org/TR/2011/WD-html5-20110525/timers.html),並相信它們根據規格行事的黑盒子性質。 – Spoike 2015-05-01 05:13:41

3

編輯:我完全誤解了這個問題。在瀏覽器中,我會使用setTimeout。如果重要的是它運行在另一個線程中,我會使用Web Workers

+1

?這不會做一個異步功能:O – markzzz 2012-03-01 13:31:52

+0

http://jsfiddle.net/5H9XT/9/由於你的答案,這不應該工作... – markzzz 2012-03-01 13:45:35

53

您可以使用計時器:

setTimeout(yourFn, 0); 

(其中yourFn是一個參考給你的函數)

,或者與underscore.js

_.defer(yourFn); 

推遲調用的函數,直到目前的調用棧已經清除, 類似於使用setTimeout with del ay爲0.有用的執行 昂貴的計算或HTML塊渲染塊沒有阻止 UI線程更新。

+1

這不起作用,我的JavaScript函數,畫在畫布上使UI不響應。 – gab06 2015-07-26 14:56:12

+1

@ gab06 - 我會說你的畫布繪圖功能因爲自己的原因而被阻塞。將其動作拆分爲許多較小的動作並使用計時器調用其中的每一個動作:您將看到這種接口確實會響應您的鼠標點擊等。 – 2015-10-31 13:16:38

5

This page引導您瞭解創建異步JavaScript函數的基礎知識。使用 通常涉及手動構建表達參數的setTimeout 或setInterval的參數在JavaScript

執行異步函數調用。

如果這樣不能解決問題,請查看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); 
} 

TEST 1(可以輸出 '1×2 3' 或「 1 2×3' 或 '1 2 3 X'):

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

TEST 2(意願人方式輸出「×1」):

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

此功能與超時0執行 - 這將模擬異步任務

+5

測試1實際上只能輸出'1 2 3 x',測試2保證每次輸出'1 x'。測試2中意外結果的原因是因爲調用了console.log(1),輸出(undefined)作爲第二個參數傳遞給async()。在測試1的情況下,我認爲你並沒有完全理解JavaScript的執行隊列。因爲每個對'console.log()'的調用發生在同一個堆棧中,'x'保證會被記錄到最後。我會對這個錯誤信息投下贊成票,但沒有足夠的代表。 – 2015-03-09 03:57:35

+1

@Joshua:看起來@fider的意思是把TEST 2寫成: 'async(function(){console.log('x')},function(){console.log(1)});'。 – nzn 2015-06-04 15:03:50

+0

是@nzn和@Joshua我的意思是'測試2 as async(function(){console.log('x')},function(){console.log(1)});' - 我已經更正了 – fider 2015-06-08 10:29:37

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保留「this」。

類似如下:

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

這是一個函數,它接受另一個函數並輸出一個運行異步的版本。

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 2016-05-28 20:21:08

+0

@ user2284570這就是關閉的目的。 '(函數(a){asyncFunction(a);})(a)' – Swivel 2016-11-22 20:26:51

+0

IIRC,你也可以在不關閉的情況下實現這個功能:'setTimeout(asyncFunction,0,a);' – Swivel 2016-11-22 20:28:01

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不提供異步功能。它只能在單個線程中工作。但大多數現代瀏覽器都提供了第二個腳本,它們在後臺執行並可以返回結果。 所以,我達成了一個解決方案,我認爲這是非常有用的異步運行一個函數,它爲每個異步調用創建一個工作。

下面的代碼包含功能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

較晚,但其在ES6出臺後顯示使用promises一個簡單的解決方案,它可以處理異步調用輕鬆了不少:

您在異步代碼放在一個新的承諾:

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

相關問題