javascript的回調函數 同步 異步

後一個任務等待前一個任務結束再執行。程序執行順序與任務排列順序一致的,同步的。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

回調函數,通常在同步情境下是最後執行的,而在異步情境下有可能不執行,由於事件沒有被觸發或者條件不知足。

回調函數的使用場合

  • 資源加載:動態加載js文件後執行回調,加載iframe後執行回調,ajax操做回調,圖片加載完成執行回調,AJAX等等。
  • DOM事件及Node.js事件基於回調機制(Node.js回調可能會出現多層回調嵌套的問題)。
  • setTimeout的延遲時間爲0,這個hack常常被用到,settimeout調用的函數其實就是一個callback的體現
  • 鏈式調用:鏈式調用的時候,在賦值器(setter)方法中(或者自己沒有返回值的方法中)很容易實現鏈式調用,而取值器(getter)相對來講很差實現鏈式調用,由於你須要取值器返回你須要的數據而不是this指針,若是要實現鏈式方法,能夠用回調函數來實現
  • setTimeout、setInterval的函數調用獲得其返回值。因爲兩個函數都是異步的,即:他們的調用時序和程序的主流程是相對獨立的,因此沒有辦法在主體裏面等待它們的返回值,它們被打開的時候程序也不會停下來等待,不然也就失去了setTimeout及setInterval的意義了,因此用return已經沒有意義,只能使用callback。callback的意義在於將timer執行的
  • 結果通知給代理函數進行及時處理。

 

回調函數的傳遞

上面說了,要將函數引用或者函數表達式做爲參數傳遞。

  • $.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

 

四、異步編程的方法三:promise對象

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

並且,它還有一個前面三種方法都沒有的好處:若是一個任務已經完成,再添加回調函數,該回調函數會當即執行。因此,你不用擔憂是否錯過了某個事件或信號。這種方法的缺點就是編寫和理解,都相對比較難。

相關文章
相關標籤/搜索