回调 订阅
回调(huí tiáo 外文名 Retracement),是指在价格上涨趋势中,价格由于上涨速度太快,受到卖方打压而暂时回落的现象。回调幅度小于上涨幅度,回调后将恢复上涨趋势。回调(huí diào 外文名 Callback),是指回调函数,编程术语。 展开全文
回调(huí tiáo 外文名 Retracement),是指在价格上涨趋势中,价格由于上涨速度太快,受到卖方打压而暂时回落的现象。回调幅度小于上涨幅度,回调后将恢复上涨趋势。回调(huí diào 外文名 Callback),是指回调函数,编程术语。
信息
定    义
指在价格上涨趋势中
分    为
回调时间、回调力度、回调量能
中文名
回调
外文名
Retracement
回调编程回调
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础,因此,下面我们着重讨论回调机制在不同软件架构中的实现。对于不同类型的语言(如结构化语言和对象语言)、平台(Win32、JDK)或构架(CORBA、DCOM、WebService),客户和服务的交互除了同步方式以外,都需要具备一定的异步通知机制,让服务方(或接口提供方)在某些情况下能够主动通知客户,而回调是实现异步的一个最简捷的途径。对于一般的结构化语言,可以通过回调函数来实现回调。回调函数也是一个函数或过程,不过它是一个由调用方自己实现,供被调用方使用的特殊函数。在面向对象的语言中,回调则是通过接口或抽象类来实现的,我们把实现这种接口的类称为回调类,回调类的对象称为回调对象。对于像C++或Object Pascal这些兼容了过程特性的对象语言,不仅提供了回调对象、回调方法等特性,也能兼容过程语言的回调函数机制。Windows平台的消息机制也可以看作是回调的一种应用,我们通过系统提供的接口注册消息处理函数(即回调函数),从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的,我们可以认为它也是回调函数的一个特例。对于分布式组件代理体系CORBA,异步处理有多种方式,如回调、事件服务、通知服务等。事件服务和通知服务是CORBA用来处理异步消息的标准服务,他们主要负责消息的处理、派发、维护等工作。对一些简单的异步处理过程,我们可以通过回调机制来实现。
收起全文
精华内容
下载资源
问答
  • js函数的回调

    万次阅读 多人点赞 2019-07-31 19:09:41
    平常的前端开发工作中,编写js时会有很多地方用到函数的回调。 最简单的例子就是: <script language="javascript" type="text/javascript"> function doSomething(callback) { if(typeof callback == ...

    平常的前端开发工作中,编写js时会有很多地方用到函数的回调。

    最简单的例子就是:

    <script language="javascript" type="text/javascript">
    function doSomething(callback) {
    if(typeof callback == "function") {
    callback();
    }
    } 
    
    function foo() {
    alert("我是回调后执行的函数");
    } 
    
    doSomething(foo); /*正确*/
    doSomething(function(){
    alert("我是回调后执行的函数");
    }); /*正确*/
    doSomething("foo"); /* 这样是不行的,传入的是一个字符串,不是一个函数名 */
    </script>

    以上只能回调没有参数的(除法你事先知道回调的函数的参数),如果函数有未知的函数,就不能如此简单的调用了。

     

    高级方法:

    1、使用javascript的call方法

    function doSomething(callback,arg1,arg2) {
    callback.call(this,arg1,arg2);
    }
    function foo(arg1,arg2) {
    alert(arg1+":"+arg2);
    }
    doSomething(foo,1,2); /* 弹出了1:2 */

     

    2、使用javascript 的 apply方法

     

    function doSomething(callback,args) {
    callback.apply(window,args);
    }
    function foo(arg1,arg2) {
    alert(arg1+":"+arg2);
    }
    doSomething(foo,[1,2,3]); /* 弹出了1:2 */

    可以看成call和apply基本一样,区别就是call只能一个个传参数,apply只能把参数放数组里传进来

    他们的第一个参数都是作用域,比如上面传了this,表示就是和doSomething这个函数一样的作用域,当然你也可以传window,表示整个window的作用域

     

    3、apply的巧妙用法

    apply也可以看作是函数的执行函数,就是用来执行某个函数的函数。所以你会发现,有时候用好apply,有很多原本繁杂的事情会变得如此简单。

    比如数组的push方法使用apply来调用:

    var arr1=[1,3,4];

    var arr2=[3,4,5];

    如果我们要把 arr2展开,然后一个一个追加到arr1中去,最后让arr1=[1,3,4,3,4,5]

    arr1.push(arr2)显然是不行的。 因为这样做会得到[1,3,4,[3,4,5]]

    我们只能用一个循环去一个一个的push(当然也可以用arr1.concat(arr2),但是concat方法并不改变arr1本身)

    var arrLen=arr2.length

    for(var i=0;i<arrLen;i++){

        arr1.push(arr2[i]);

    }

    自从有了Apply,事情就变得如此简单

    Array.prototype.push.apply(arr1,arr2)

    一行代码就解决了,原理能看的出来,Array.prototype.push是指数组的push函数,apply(arr1,arr2)说明arr1是作用域,就等同于是arr1调用了数组的push函数,

    而且arr1的确就是个数组,所以可以调用,arr2表示入参的数组。所以,以上语句等同于:arr1.push(3,4,5)。(push函数支持传递多个入参,这也是这里可以使用apply的前提条件)

    以上语句也可以写成:arr1.push.apply(arr1,arr2); 两者完全等效,因为arr1.push表示arr1的push函数,也就是数组的push函数。

    如果使用call就是这样Array.prototype.push.call(arr1,arr2[0],arr2[1]...),显然还是apply合适。

    要是你还问,那直接用arr1.push(3,4,5)不就行了,那已经暴露了你的智商,arr2又不是不可以变,下次不是[3,4,5]了呢。

     

    还有获取数组中,最大的那个数字,也可以使用apply调用Math.max函数

    var arr1=[1,3,4];

    alert(Math.max.apply(window,arr1)); /* 作用域可以不是window,就算是null都行,Math.max.apply(this,arr1),Math.max.apply(null,arr1) */

     

    4、工作中函数回调的实际例子

    有了上面的基础,就能看的懂工作中封装好的js的回调函数了

    背景:页面A需要使用页面B来选择某个项目,然后带回这个项目的信息给页面A,页面A根据这些信息丰富自己。

    页面A:

    noticeInfo = {
    selectProject: function () {
    var win = newsee.ui.window
    win.show('项目列表', '../Project/ProjectSelectList.html?callback=noticeInfo.setProjectInfo', { size: win.winSizeType.big })
    //在当前页面弹出框,框里面是另一个页面,地址后面带上需要回调的函数名
    //注意这两个页面其实都是在一个页面里面的,并不是像window.open()那样出现了新窗口,所以两个页面的js都是可见的
    },
    setProjectInfo: function (obj) {
    //回调函数,将选择好的项目对象传进来,然后丰富自己的页面
    $('#projectName').val(obj.name)
    $('#projectID').val(obj.id)
    }
    }

    页面B:

    function SelectBack() {
    var callback = newsee.util.url.getQuery('callback'); //获取页面参数callback,这里获取到的是"noticeInfo.setProjectInfo",是个字符串
    var arr = newsee.ui.grid.getSelectedBack('datagrid') //获取选择的项目,这个不用深究
    if (!arr.length) {
    return newsee.ui.window.alert('请选择项目!')
    }
    newsee.util.url.back(callback, arr[0]) //重点来了,这里执行回调,将需要回调的函数名和入参传进来,arr[0]就是选择的项目的对象的数组了(它也是个数组,里面就一个对象)
    }

    newsee.util.url.back函数如下:

    back : function (funcName) {
    // / <param name="funcName" type="String">返回时执行的方法,一般为重新绑定</param>
    
    var isWindow = typeof $$winClose === 'function',// 是否为弹窗
    args // 弹窗返回方法参数
    
    if (isWindow) {// 弹窗的返回方法
    $$winClose()
    
    args = [].slice.call(arguments) //arguments大家应该都知道的吧,它可以用来获取函数的实参,它类似数组又不是数组,这句代码就是把它转换成数组,因为apply的入参需要是个数组才行
    //args现在里面有两个元素,args[0]=callback,就是之前传进来的回调函数名,args[1]=arr[0],就是回调函数的入参
    newsee.callFunc.apply(newsee, args) //执行 newsee.callFunc 函数,作用域就是newsee自己(等同于newsee自己调用callFunc函数),参数是args
    } 
    }

    newsee.callFunc函数如下:

    callFunc: function(funcName, arg) {
    var func = typeof funcName === 'function' ? funcName : this.findItem(window, funcName) //上面我有提到过,doSomething("foo"); 传入的是一个字符串,不是一个函数名,所以无法执行
    //同样的道理,现在funcName=args[0]=callback="noticeInfo.setProjectInfo",是个字符串,不能直接调用apply,需要变成函数
    //这句话就是用来判断funcName是不是一个函数,如果不是,就在window作用域里根据funcName找到这个函数,然后赋给func
    if (typeof func === 'function') {
    //此时func已经是个函数了,就是页面A里定义的noticeInfo.setProjectInfo()
    try {
    return func.apply(window, arg) //执行需回调的函数,作用域依然是window,反正这个函数在window里肯定能找到,参数就是arg=args[1]=arr[0],即之前在页面B获取到的项目对象
    }
    catch (e) {
    console.error(e)
    }
    }
    }

    ok,需回调的函数就这样被执行了,至于怎么根据字符串形式的函数名获取这个函数,看下面。

    //findItem函数如下:
    findItem: function(data, key) {
    // / <summary>获取对象指定键的值</summary>
    if (this.include(data, key)) { //data这里就是传进来的window,注意window就是一个对象,首先判断window对象里是否存在"noticeInfo.setProjectInfo"这个属性
    return eval('data.' + key) //如果存在,就执行"data.noticeInfo.setProjectInfo",这样就获取到了这个函数了。(eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码)
    }
    }
    //include函数如下:
    include: function(data, key) {
    // / <summary>判断对象是否存在键值</summary>
    if (data == null || typeof data !== 'object' || !key || typeof key !== 'string') {
    return false
    }
    var keys = key.split('.'),
    item = data,
    result = true
    keys.forEach(function(k) {
    if (item != null && typeof item === 'object' && k in item) {
    //依次循环遍历,第一次item = data,那就是window这个对象,k="noticeInfo",window[noticeInfo]是存在的,因为在页面A里定义了noticeInfo这么一个对象
    //第二次循环,item=window.noticeInfo,k="setProjectInfo",window.noticeInfo[setProjectInfo]也是存在的,因为在页面A里也定义了setProjectInfo这么一个函数
    //这里没有第三次循环了,所以最后返回是true,说明window对象里存在"noticeInfo.setProjectInfo"这个属性,接下来使用eval()拿到它即可
    item = item[k]
    } else {
    return result = false
    }
    })
    
    return result
    }

    对eval() 函数也介绍一下:

    eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。

    返回值就是通过计算 string 得到的值(如果有的话)。如:

    eval("x=10;y=20;document.write(x*y)") //输出 200

    document.write(eval("2+2")) //输出 4

    var x=10

    document.write(eval(x+17)) //输出 27

    所以上面的eval('data.' + key)就是执行"data.noticeInfo.setProjectInfo"这个字符串,

    因为data在这里就是指window,所以返回值就是window.noticeInfo.setProjectInfo()这个函数

     

    其实可以在简单一点,根本没必要使用eval()来获取这个函数,因为在include函数里,item就已经是window.noticeInfo.setProjectInfo这个对象了,这个对象就是我们想要的函数。

    (在js中函数也是对象,函数名就是这个函数的引用,就和地址差不多)

    既然都拿到这个函数了,直接返回不就行了,所以上面的include()和findItem可以这样简化:

    include: function(data, key) {
    if (data == null || typeof data !== 'object' || !key || typeof key !== 'string') {
    }else{
    var keys = key.split('.'),
    item = data,
    result = true
    keys.forEach(function(k) {
    if (item != null && typeof item === 'object' && k in item) {
    item = item[k]
    } else {
    result = false;
    }
    })
    if(result)
    return item
    }
    },
    findItem: function(data, key) {
    return this.include(data, key)
    }

    经过测试,发现这两个根据字符串形式的函数名获取函数的方法都可以达到一模一样的效果。

    ------------------------------------------------------------------------声明-------------------------------------------------------------
    以上部分是copy别人的,部分是自己的理解!
    本人水平有限,如有不对之处,还望大神指正,谢谢!

    展开全文
  • 回调函数 同步回调 异步回调

    千次阅读 2018-06-11 16:56:34
    回调函数一般是在封装接口的时候,回调显得特别重要,我们首先假设有两个程序员在写代码,A程序员写底层驱动接口,B程序员写上层应用程序,然而此时底层驱动接口A有一个数据d需要传输给B,此时有两种方式: ...

    目录

    回调函数

    同步回调和异步回调

    同步回调

    异步回调


    回调函数

    回调函数一般是在封装接口的时候,回调显得特别重要,我们首先假设有两个程序员在写代码,A程序员写底层驱动接口,B程序员写上层应用程序,然而此时底层驱动接口A有一个数据d需要传输给B,此时有两种方式: 

    1. A将数据d存储好放在接口函数中,B自己想什么时候去读就什么时候去读,这就是我们经常使用的函数调用,此时主动权是B。 
    2. A实现回调机制,当数据变化的时候才将通知B,你可以来读取数据了,然后B在用户层的回调函数中读取速度d,完成OK。此时主动权是A。 

    很明显第一种方法太低效了,B根本就不知道什么时候该去调用接口函数读取数据d。而第二种方式由于B的读取数据操作是依赖A的,只有A叫B读数据,那么B才能读数据。也即是实现了中断读取。 
    那么回调是怎么实现的呢,其实回调函数就是一个通过函数指针调用的函数。如果用户层B把函数的指针(地址)作为参数传递给底层驱动A,当这个指针在A中被用为调用它所指向的函数时,我们就说这是回调函数。 
    注意:是在A中被调用,这里看到尽管函数是在B中,但是B却不是自己调用这个函数,而是将这个函数的函数指针通过A的接口函数传自A中了,由A来操控执行,这就是回调的意义所在。 

    同步回调和异步回调

    首先,理解几个概念:

    1. 回调可以是同步也可以是异步
    2. 同步可以是单线程也可以是多线程

       异步必须是多线程或多进程(每个进程可以是单线程) ==> 换句话说,异步必须依靠多线程或多进程才能完成

     

    同步回调:把函数b传递给函数a。执行a的时候,回调了b,a要一直等到b执行完才能继续执行;

    异步回调:把函数b传递给函数a。执行a的时候,回调了b,然后a就继续往后执行,b独自执行。

    直接看例子

    A.h

    #ifndef A_H
    #define A_H
    typedef void (*pcb)(int a); //函数指针定义,后面可以直接使用pcb,方便
    void SetCallBackFun(int a, pcb callback);
    #endif

    同步回调

    synA.c

    #include <stdio.h>
    #include "A.h"
    
    //-----------------------底层实现A-----------------------------
    
    //留给应用层B的接口函数
    void SetCallBackFun(int a, pcb callback)
    {
        printf("A:start\n");
        callback(a);
        printf("A:end\n");
    }
    

    synB.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include "A.h"
    //-----------------------应用者B-------------------------------
    void fCallBack(int a)       // 应用者增加的函数,此函数会在A中被执行
    {
        //do something
        printf("B:start\n");
        printf("a = %d\n",a);
        sleep(5);
        printf("B:end\n");
    }
    
    int main(void)
    {
        SetCallBackFun(4,fCallBack);
        return 0;
    }

    异步回调

    asynA.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include "A.h"
    
    //-----------------------底层实现A-----------------------------
    typedef struct parameter{
        int a ;
        pcb callback;
    }parameter;
    
    void* callback_thread(void *p1)//此处用的是一个线程
    {
        //do something
        parameter* p = (parameter*)p1 ;
    
        sleep(5);
        p->callback(p->a);//函数指针执行函数,这个函数来自于应用层B
    
    }
    
    //留给应用层B的接口函数
    void SetCallBackFun(int a, pcb callback)
    {
        printf("A:start\n");
        parameter *p = malloc(sizeof(parameter)) ;
        p->a  = a;
        p->callback = callback;
        
        //创建线程
        pthread_t pid;
        pthread_create(&pid,NULL,callback_thread,(void *) p);
        printf("A:end\n");
    	
        //阻塞,等待线程pid结束,才往下走
        pthread_join(pid,NULL);
    }
    

    asynB.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include "A.h"
    //-----------------------应用者B-------------------------------
    void fCallBack(int a)       // 应用者增加的函数,此函数会在A中被执行
    {
        //do something
        printf("B:start\n");
        printf("a = %d\n",a);
        sleep(5);
        printf("B:end\n");
    }
    
    
    int main(void)
    {
        SetCallBackFun(4,fCallBack);
        return 0;
    }

    运行结果比较: