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

并且,它还有一个前面三种方法都没有的好处:若是一个任务已经完成,再添加回调函数,该回调函数会当即执行。因此,你不用担忧是否错过了某个事件或信号。这种方法的缺点就是编写和理解,都相对比较难。

相关文章
相关标签/搜索