後一個任務等待前一個任務結束再執行。程序執行順序與任務排列順序一致的,同步的。javascript
參考:php
http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.htmlhtml
https://segmentfault.com/q/1010000000140970java
在JavaScript中,回調函數具體的定義爲:函數A做爲參數(函數引用)傳遞到另外一個函數B中,而且這個函數B執行函數A。咱們就說函數A叫作回調函數。若是沒有名稱(函數表達式),就叫作匿名回調函數。jquery
所以callback 不必定用於異步,通常同步(阻塞)的場景下也常常用到回調,好比要求執行某些操做後執行回調函數。ajax
var func1=function(callback){ //do something. (callback && typeof(callback) === "function") && callback(); } var func2=function(){} func1(func2);
function f1(callback){ setTimeout(function () { // f1的任務代碼 callback(); }, 1000); } f1(f2)
採用這種方式,咱們把同步操做變成了異步操做,f1不會堵塞程序運行,至關於先執行程序的主要邏輯,將耗時的操做推遲執行。編程
回調函數的優勢是簡單、容易理解和部署,缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程會很混亂,並且每一個任務只能指定一個回調函數。segmentfault
異步回調的例子:api
$(document).ready(callback); $.ajax({ url: "test.html", context: document.body }).done(function() { $(this).addClass("done"); }).fail(function() { alert("error"); }).always(function() { alert("complete"); }); /** 注意的是,ajax請求確實是異步的,不過這請求是由瀏覽器新開一個線程請求,當請求的狀態變動時,若是先前已設置回調,這異步線程就產生狀態變動事件放到 JavaScript引擎的處理隊列中等待處理。見:http://www.phpv.net/html/1700.html */
回調何時執行promise
回調函數,通常在同步情境下是最後執行的,而在異步情境下有可能不執行,由於事件沒有被觸發或者條件不知足。
回調函數的使用場合
回調函數的傳遞
上面說了,要將函數引用或者函數表達式做爲參數傳遞。
$.get('myhtmlpage.html', myCallBack);//這是對的 $.get('myhtmlpage.html', myCallBack('foo', 'bar'));//這是錯的,那麼要帶參數呢? $.get('myhtmlpage.html', function(){//帶參數的使用函數表達式 myCallBack('foo', 'bar'); });
另外,最好保證回調存在且必須是函數引用或者函數表達式:
(callback && typeof(callback) === "function") && callback();
事件監聽:
var doc = $(document); function f2(){ console.log("done"); } function f1(){ setTimeout(function(){ doc.trigger("done") }, 1000); } doc.on("done", f2); f1();
發佈訂閱:和事件監聽如出一轍啊。
觀察者模式所作的工做就是在解耦,讓耦合的雙方都依賴於抽象,而不是依賴於具體。從而使得各自的變化都不會影響到另外一邊的變化。
var Observable = {//doc callbacks: [], add: function(fn) { this.callbacks.push(fn); }, fire: function() { this.callbacks.forEach(function(fn) { fn(); }) } } Observable.add(function() { alert(1) }) Observable.add(function() { alert(2) }) Observable.fire(); // 1, 2
Promises對象是CommonJS工做組提出的一種規範,目的是爲異步編程提供統一接口。
簡單說,它的思想是,每個異步任務返回一個Promise對象,該對象有一個then方法,容許指定回調函數。好比,f1的回調函數f2,能夠寫成:
f1().then(f2);
f1要進行以下改寫(這裏使用的是jQuery的實現):
function f1(){
var dfd = $.Deferred();
setTimeout(function () {
// f1的任務代碼
dfd.resolve();
}, 500);
return dfd.promise;
}
這樣寫的優勢在於,回調函數變成了鏈式寫法,程序的流程能夠看得很清楚,並且有一整套的配套方法,能夠實現許多強大的功能。
好比,指定多個回調函數:
f1().then(f2).then(f3);
再好比,指定發生錯誤時的回調函數:
f1().then(f2).fail(f3);
並且,它還有一個前面三種方法都沒有的好處:若是一個任務已經完成,再添加回調函數,該回調函數會當即執行。因此,你不用擔憂是否錯過了某個事件或信號。這種方法的缺點就是編寫和理解,都相對比較難。