API的支付宝 回调 api函数怎么用

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
一名小小的软件工程师
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
  我们经常在C++设计时通过使用回调函数可以使有些应用(如定时器事件回调处理、用回调函数记录某操作进度等)变得非常方便和符合逻辑,那么它的内在机制如何呢,怎么定义呢?它和其它函数(比如钩子函数)有何不同呢?  使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。 而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。至于如何定义回调函数,跟具体使用的 API函数有关,一般在帮助中有说明回调函数的参数和返回值等。C++中一般要求在回调函数前加CALLBACK(相当于FAR PASCAL),这主要是说明该函数的调用方式。  至于钩子函数,只是回调函数的一个特例。习惯上把与SetWindowsHookEx函数一起使用的回调函数称为钩子函数。也有人把利用VirtualQueryEx安装的函数称为钩子函数,不过这种叫法不太流行。  也可以这样,更容易理解:回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用。为此,你需要做三件事:  1. 声明;  2. 定义;  3. 设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于系统调用。  声明和定义时应注意:回调函数由系统调用,所以可以认为它属于WINDOWS系统,不要把它当作你的某个类的成员函数。 二、回调函数、消息和事件例程  调用(calling)机制从汇编时代起已经大量使用:准备一段现成的代码,调用者可以随时跳转至此段代码的起始地址,执行完后再返回跳转时的后续地址。 此调用机制并非完美。回调函数就是一例。函数之类本是为调用者准备的美餐,其烹制者应对食客了如指掌,但实情并非如此。例如,写一个快速排序函数供他人调 用,其中必包含比较大小。麻烦来了:此时并不知要比较的是何类数据--整数、浮点数、字符串?于是只好为每类数据制作一个回调函数地址,并通知调用者:君需自己准备一个比较函数,其中包含两个指针类参数,函数要比较此二指针所指数据之大小,并由函数返回值 说明比较结果。排序函数借此调用者提供的函数来比较大小,借指针传递参数,可以全然不管所比较的数据类型。被调用者回头调用调用者的函数(够咬嘴的),故 称其为回调(callback)。  回调函数使程序结构乱了许多。Windows API 函数集中有不少回调函数,尽管有详尽说明,仍使初学者一头雾水。恐怕这也是无奈之举。  无论何种事物,能以树形结构单向描述毕竟让人舒服些。如果某家族中孙辈又是某祖辈的祖辈,恐怕无人能理清其中的头绪。但数据处理之复杂往往需要构成网状结构,非简单的客户/  Windows 系统还包含着另一种更为广泛的回调机制,即消息机制。消息本是 Windows 的基本控制手段,乍看与函数调用无关,其实是一种变相的函数调用。发送消息的目的是通知收方运行一段预先准备好的代码,相当于调用一个函数。消息所附带的 WParam 和 LParam 相当于函数的参数,只不过比普通参数更通用一些。应用程序可以主动发送消息,更多情况下是坐等 Windows 发送消息。一旦消息进入所属消息队列,便检感兴趣的那些,跳转去执行相应的消息处理代码。操作系统本是为应用程序服务,由应用程序来调用。而应用程序一旦 启动,却要反过来等待操作系统的调用。这分明也是一种回调,或者说是一种广义回调。其实,应用程序之间也可以形成这种回调。假如进程 B 收到进程 A 发来的消息,启动了一段代码,其中又向进程 A 发送消息,这就形成了回调。这种回调比较隐蔽,弄不好会搞成递归调用,若缺少终止条件,将会利用消息也可以构成狭义回调。上面所举排序函数一例,可以把回调函数地址换成窗口 handle。如此,当需要比较数据大小时,不是去调用回调函数,而是借 API 函数 SendMessage 向指定窗口发送消息。收到消息方负责比较数据大小,把比较结果通过消息本身的返回值传给消息发送方。所实现的功能与回调函数并无回调函数中包含文件处理之类的低 速处理,调用方等不得,需要把同步调用改为异步调用,去启动一个单独的线程,然后马上执行后续代码,其余的事让线程慢慢去做。一个替代办法是借 API 函数 PostMessage 发送一个异步消息,然后立即执行后续代码。这要比自己搞个线程省事许多,而且更安全。  如今我们是活在一个 object 时代。只要与编程有关,无论何事都离不开 object。但 object 并未消除回调,反而把它发扬光大,弄得到处都是,只不过大都以事件(event)的身份出现,镶嵌在某个结构之中,显得更正统,更容易被人接受。应用程序 要使用某个构件,总要先弄清构件的属性、回调函数地址没什么区别。  不过,此种回调方式比传统回调函数要高明许多。首先,它把让人不太舒服的回调函数变成一种自然而然的处理例程,使编程者顿觉气顺。再者,地址是一个危险的 东西,用好了可使程序加速,用不好处处是陷阱,程序随时都会崩溃。现代编程方式总是想法把地址隐藏起来(隐藏比较彻底的如 VB 和 Java),其代价是降低了程序效率。事件例程(?)使编程者无需直接操作地址,但并不会使程序减速。三、精妙比喻:回调函数还真有点像您随身带的BP机:告诉别人号码,在它有事情时Call您。  回调用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调,例如作为一个驱动,是一个底层,他在收到一个数据时,除了 完成本层的处理四、调用方式  软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕 才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它 的调用方向刚好相反,接口的对于不同类型的  对于一般的结构化语言,可以通过回调函数来实现回调。回调函数也是一个函数或过程,不过它是一个由调用方自己实现,供被调用方使用的特殊函数。  在面向对象的语言中,回调则是通过接口或抽象类来实现的,我们把实现这种接口的类成为回调类,回调类的对象成为回调对象。对于象C++或Object Pascal这些兼容了过程特性的对象语言,不仅提供了回调对象、回调回调函数机制。  Windows平台的消息机制也可以看作是回调的一种应用,我们通过系统提供的接口注册消息处理函数(即回调函数),从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的,我们可以认为它也是回调函数的一个特例。  对于分布式组件  下面我们集中比较具有代表性的语言(C、Object Pascal)和架构(CORBA)来分析回调的实现方式、具体作用等。  过程语言中的回调(C)  (1 )函数指针  回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此,要实现回调,必须首先定义函数指针,请看下面的例子: void Func(char *s);// 函数原型  void (*pFunc) (char *);//函数指针  可以看出,函数的定义和函数指针的定义非常类似。  一般的化,为了简化函数指针类型的变量定义,提高程序的可读性,我们需要把函数指针类型自定义一下。 typedef void(*pcb)(char *);  回调函数可以象普通函数一样被程序调用,但是只有它被当作参数传递给被调函数时才能称作回调函数.  被调函数的例子: void GetCallBack(pcb callback) { /*do something*/ }用户在调用上面的函数时,需要自己实现一个pcb类型的回调函数: void fCallback(char *s) { /* do something */ }  然后,就可以直接把fCallback当作一个变量传递给GetCallBack,  GetCallBack(fCallback);  如果赋了  (2 )参数传递规则  到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或 者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。  将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:  // 被调用函数是以int为参数,以int为返回值  __stdcall int callee(int);  // 调用函数以函数指针为参数  void caller( __cdecl int(*ptr)(int));  // 在p中企图存储被调用函数地址的非法操作  __cdecl int(*p)(int) = // 出错  指针p和callee()的类型不兼容,因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列  (3 )应用举例  C  快速排序函数原型: void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));  二分搜索函数原型: void *bsearch(const void *key, const void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));  其中fcmp就是一个回调函数的变量。  下面给出一个具体的例子: #include &stdio.h& #include &stdlib.h& int sort_function( const void *a, const void *b); int list[5] = { 54, 21, 11, 67, 22 }; int main(void) {
qsort((void *)list, 5, sizeof(list[0]), sort_function);
for (x = 0; x & 5; x++)
printf("%i\n", list[x]);
return 0; } int sort_function( const void *a, const void *b) {
return *(int*)a-*(int*)b; }回调函数例子1#include&stdio.h&#include&stdlib.h&void perfect(int n){ int i=1;& & & & int count=0;&for(i=1;i&n;i++)&{& & & if(0==n%i) {
count+=i; }&} if(count==n)
printf("%d是完数\n",n); else printf("%d不是完数\n",n);}void myCallback(void (*perfect)(int ),int n){& perfect(n);}int main(){
printf("请输入一个正整数\n"); scanf("%d",&n); myCallback(perfect,n); return 0;&}
阅读(2796)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'C/C++ 回调函数',
blogAbstract:'一、回调函数  我们经常在C++设计时通过使用回调函数可以使有些应用(如定时器事件回调处理、用回调函数记录某操作进度等)变得非常方便和符合逻辑,那么它的内在机制如何呢,怎么定义呢?它和其它函数(比如钩子函数)有何不同呢?  使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。 而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。至于如何定义回调函数,跟具体使用的 API函数有关,一般在帮助中有说明回调函数的参数和返回值等。C++中一般要求在回调函数前加CALLBACK(相当于FAR PASCAL),这主要是说明该函数的调用方式。  至于钩子函数,只是回调函数的一个特例。习惯上把与S',
blogTag:'c/c++,回调函数',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:5,
publishTime:7,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:1,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:true,
hostIntro:'一名小小的软件工程师',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}(钩子应用)按键【回调函数】解决方案 _ 综合讨论 - 按键精灵论坛
腾讯微博:
软件版本:2014.05软件大小:76.8M更新时间:2-21
软件版本:3.2.3软件大小:62.5M更新时间:05-03
软件版本:1.2.5软件大小:29.2M更新时间:04-18
软件版本:1.1.0软件大小:12.3M更新时间:12-29
查看: 1962|回复: 7
[心得分享]
按键精灵开发者4级可通过提升认证等级来升级勋章:爱心大使积极帮助新手(可向管理员进行申请)教程达人教程达人黄金之翼黄金之翼勋章潜水员按键潜水员勋章天使勋章天使一样的用户
本帖最后由 风__琪仙 于
16:57 编辑 按键【回调函数】解决方案我们的QQ群是:《《《
可能大家对API函数的使用已经有了一些了解,那么回调函数这个词应该不是太陌生吧。著名的钩子函数就用到了它,但是在按键调用的时候就遇到难题了,钩子函数其中一个参数需要提供子程序地址,也就是回调函数的指针,按键精灵明显是不支持的,那该怎么办?
以下内容如果有冒犯,请谅解,请原谅俺求知之心。
经过一番功夫,终于发现了突破口,大家可以使用SendMessageA函数向按键精灵QUI窗口发送1127消息试试,你会发现窗口过程调用了窗口加载完毕事件子程序,事情到此有了些进展了。而实际上也可以使用GetWindowLong函数取得窗口过程函数地址,然后使用OD跟踪可以看到QUI窗口过程的全貌。
初步问题解决了,但是回调函数它还有3个参数,得怎么办?这里只能拐个弯子写个标准DLL来实现咯。请看以下代码实现键盘钩子的过程。//回调函数辅助Dll
Declare Function GetSubPtr Lib
"Callback.dll" Alias
"GetSubPtr"(ByVal hwnd As Long) As Long//取函数地址
Declare Function SetHook Lib
"Callback.dll" Alias
"SetHook"(ByVal 钩子句柄 As Long) As Long
Declare Function GetPar1 Lib
"Callback.dll" Alias
"GetPar1"() As Long
Declare Function GetPar2 Lib
"Callback.dll" Alias
"GetPar2"() As Long
Declare Function GetPar3 Lib
"Callback.dll" Alias
"GetPar3"() As Long
//功能演示
Declare Function 设置系统钩子_ Lib
"user32.dll" Alias
"SetWindowsHookExA"(ByVal 钩子类型 As Long,ByVal 回调函数地址 As Long,ByVal 实例句柄 As Long,ByVal 线程ID As Long) As Long
Declare Function 取模块句柄_ Lib
"kernel32.dll" Alias
"GetModuleHandleA"(ByVal 模块名 As String) As Long
Declare Function 释放系统钩子_ Lib
"user32.dll" Alias
"UnhookWindowsHookEx"(ByVal 钩子句柄 As Long) As Long
Event Form1.LoadOver
参数一= GetPar1()
参数三= GetPar3()
Select Case 参数二
Call Plugin.Msg.Tips("按下")
Call Plugin.Msg.Tips("放开")
End Select
Event Form1.Load
钩子句柄 = 设置系统钩子_(13, GetSubPtr(Form1.Hwnd), 取模块句柄_(0), 0)
call SetHook(钩子句柄)
If 钩子句柄>0 Then
MessageBox "键盘钩子安装成功~"
MessageBox "安装失败!"
Event Form1.UnLoad
call 释放系统钩子_ (钩子句柄)
MessageBox "脚本已经停止!钩子已经释放!!"
复制代码以上代码大家会发现一个神奇之处,明明这几个子程序没有联系在一起,怎么会有用呢?这也正是一大妙处。首先介绍一下[backcolor=rgb(247, 247, 247)]Callback.dll 这个DLL是我自己写的,附带告诉一下大家如何使用标准的DLL吧。习惯了用Com和按键接口的插件,标准的DLL用法可能大家还不知,其实标准DLL的使用也是很简单滴。只要把标准的DLL文件放到按键精灵的插件目录,按键精灵在运行时会自动加载,然后只需要在使用时定义就可以了,如上代码,像定义API函数一样定义即可。CALLBack.dll 包含了5个函数。GetSubPtr 取回调函数地址。参数【窗口句柄】
返回【回调函数地址】 好进行下一步使用参数是子程序指针的API函数SetHook
此属性适用于钩子函数,使用钩子函数时必需使用用于呼叫钩子链的下一个钩子。参数是【钩子句柄】不用为0GetPar1
取得回调函数的第一个参数GetPar2
取得回调函数的第二个参数GetPar3
取得回调函数的第三个参数
在dll内部会调用按键中的“Form1.LoadOver”子程序,然后在“Form1.LoadOver”子程序中取回参数,写功能代码,钩子处理代码,这样就实现了函数的回调。但是缺点也是明显的,那就是只能回调“Form1.LoadOver”子程序,想实现真正的回调还得要工程师多给点支持!!
代码使用步骤:下载(, 下载次数:61)(CALLBack.dll),解压到按键插件目录,复制以上代码到按键精灵源代码区,设置QUI界面,调试时点击"自定义界面按钮",然后随意按键键盘,可以看到桌面右下角有气泡提示。
由于水平有限走此弯路,同时也希望按键精灵能多公开一些东西给大家使用。
黄瓜勋章(永久)体验商业小精灵活动的奖励双蛋勋章(30天)双蛋活动勋章(30天)马年勋章(360天) 马年纪念勋章(360天)双十一绝版纪念勋章双十一绝版纪念勋章按键精灵开发者4级可通过提升认证等级来升级勋章:
支持分享,
淡定还是蛋疼,这是个问题~~~
playandhappy
学有所成学有所成勋章,新手步入按键学堂的第一枚勋章爱心大使积极帮助新手(可向管理员进行申请)兔年勋章(360天)兔年纪念勋章(360天)按键精灵开发者6级可通过提升认证等级来升级勋章:骨灰级按键用户3年以上的按键论坛用户黄金之翼黄金之翼勋章按键明星按键明星勋章潜水员按键潜水员勋章天使勋章天使一样的用户官方脚本作者工会官方脚本作者工会专属勋章 认证考霸(90天)参加考霸活动,对认证考了十次以上用户的鼓励幸运草勋章(永久)商业小精灵限时活动勋章。单身汪勋章单身贵族的专属勋章
厉害,而且无隐藏回复内容。钻研精神敬佩
技能列表:各类自动化,TCP通信,POST,办公,Sqlserver,网页API要价较高,慎入只接单,其他问题勿扰
按键精灵开发者3级可通过提升认证等级来升级勋章:按键会员(季)按键会员绑定账号后自动赠送脚本作者商业小精灵作者绑定账号后自动赠送爱心大使积极帮助新手(可向管理员进行申请)
还不如真接写到DLL里面,反正都是借工具实现。不过楼主精神可嘉
&font color=&Red&&&strong&接各种单QQ:&/strong&&/font&
按键精灵开发者4级可通过提升认证等级来升级勋章:
最近也在看回调函数的内容,getref可用。。。。钩子,还得再学习
按键精灵开发者6级(新浪V认证)通过新浪微博加V后自动发放,并替换普通的认证6级勋章。 抗战胜利70周年勋章抗战胜利70周年活动的专属勋章
勾子是个好东西
学有所成学有所成勋章,新手步入按键学堂的第一枚勋章月全勤论坛自然月(如8.1-8.31)签到满勤,系统会在月底自动判断发放。
按键精灵开发者6级可通过提升认证等级来升级勋章:
太棒了,解决了困扰多时的难题!谢谢分享!javascript的回调函数应用示例
字体:[ ] 类型:转载 时间:
回调函数就是一个通过函数指针调用的函数。下面以示例的方式为大家介绍下其具体的使用
回调函数概念:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。 JS Api 里这样解释:A callback is a function that is passed as an argument to another function and is executed after its parent function has completed. 使用回调函数的原因:可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。 考虑一个这样的例子: 假如某个项目的底层和高层是由不同的人员协同完成.底层负责数据的存取,高层负责数据的表示.当高层要用到某个模块的数据,于是他对底层人员说,我需要你们提供满足某种需求的数据,你给我提供一个接口. 底层的人员说:我给你提供数据,怎么展示和处理则是你的事情.我不可能为你每个需求都提供一个数据接口,我给你提供一个通过的接口.你得到数据,然后自己写函数去展示.由是经过协商,双方提供了一个这样的接口:
代码如下: //data表示底层提供的数据源,funcName表示高层的调用函数 function(data,funcName){ 1.data属于情形1,由底层处理; 2.data属于情形2,由高层处理,怎么处理呢?利用高层提供的函数funcName处理 ..... }
我可能还没说清楚,我们看个例子一下子就明白了
代码如下: //假如提供的数据源是一整数,为某学生的分数,当num&=0,由底层处理,当n&0时由高层处理. //将下面这个函数拷贝下来存盘为1.js function f(num,callback){ if(num&0) { alert("调用低层函数处理!"); alert("分数不能为负,输入错误!"); }else if(num==0){ alert("调用低层函数处理!"); alert("该学生可能未参加考试!"); }else{ alert("调用高层函数处理!"); callback(); } }
代码如下: //将下面这个test.html文件存盘与1.js在一个目录下: &!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"& &html& &head& &meta http-equiv="Content-Type" content="text/ charset=gb2312"& &script src="1.js" type="text/javascript"&&/script& &title&无标题文档&/title& &script type="text/javascript"& function test(){ var p=document.getElementById("pp"); pp.innerText=""; var num=document.getElementById("score"). f(num,function(){ //匿名高层处理函数 if(num&60) alert("未及格!"); else if(num&=90) alert("该生成绩优良!"); else alert("该生成绩优秀!"); }) pp.innerText="by since1978 qq558064!" } &/script& &/head& &body& &p& 回调函数示例:当学生成绩score&=0分时候,由底层处理;当score&0时,由高层处理。 &/p& 请输入学生成绩&input type="text" id="score"& &input type="button" onClick="test()" value=" 看看结果"& &p id="pp"&&/p& &/body& &/html&
运行此文件,可以看到效果
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具使用 promise 替代回调函数
理解 Promise 概念,为什么需要 promise
学习 q 的 API,利用 q 来替代回调函数( )
第五课( )讲述了如何使用 async 来控制并发。async 的本质是一个流程控制。其实在异步编程中,还有一个更为经典的模型,叫做 Promise/Deferred 模型。
本节我们就来学习这个模型的代表实现:
首先,我们思考一个典型的异步编程模型,考虑这样一个题目:读取一个文件,在控制台输出这个文件内容。
var fs = require('fs');
fs.readFile('sample.txt', 'utf8', function (err, data) {
console.log(data);
看起来很简单,再进一步: 读取两个文件,在控制台输出这两个文件内容。
var fs = require('fs');
fs.readFile('sample01.txt', 'utf8', function (err, data) {
console.log(data);
fs.readFile('sample02.txt', 'utf8', function (err,data) {
console.log(data);
要是读取更多的文件呢?
var fs = require('fs');
fs.readFile('sample01.txt', 'utf8', function (err, data) {
fs.readFile('sample02.txt', 'utf8', function (err,data) {
fs.readFile('sample03.txt', 'utf8', function (err, data) {
fs.readFile('sample04.txt', 'utf8', function (err, data) {
这段代码就是臭名昭著的邪恶金字塔(Pyramid of Doom)。可以使用async来改善这段代码,但是在本课中我们要用promise/defer来改善它。
promise基本概念
先学习promise的基本概念。
promise只有三种状态,未完成,完成(fulfilled)和失败(rejected)。
promise的状态可以由未完成转换成完成,或者未完成转换成失败。
promise的状态转换只发生一次
promise有一个then方法,then方法可以接受3个函数作为参数。前两个函数对应promise的两种状态fulfilled, rejected的回调函数。第三个函数用于处理进度信息。
promiseSomething().then(function(fulfilled){
//当promise状态变成fulfilled时,调用此函数
},function(rejected){
//当promise状态变成rejected时,调用此函数
},function(progress){
//当返回进度信息时,调用此函数
学习一个简单的例子:
var Q = require('q');
var defer = Q.defer();
* 获取初始promise
* @private
function getInitialPromise() {
return defer.
* 为promise设置三种状态的回调函数
getInitialPromise().then(function(success){
console.log(success);
},function(error){
console.log(error);
},function(progress){
console.log(progress);
defer.notify('in progress');//控制台打印in progress
defer.resolve('resolve');
//控制台打印resolve
defer.reject('reject');
//没有输出。promise的状态只能改变一次
promise的传递
then方法会返回一个promise,在下面这个例子中,我们用outputPromise指向then返回的promise。
var outputPromise = getInputPromise().then(function (fulfilled) {
}, function (rejected) {
现在outputPromise就变成了受 function(fulfilled) 或者 function(rejected)控制状态的promise了。怎么理解这句话呢?
当function(fulfilled)或者function(rejected)返回一个值,比如一个字符串,数组,对象等等,那么outputPromise的状态就会变成fulfilled。
在下面这个例子中,我们可以看到,当我们把inputPromise的状态通过defer.resovle()变成fulfilled时,控制台输出fulfilled.
当我们把inputPromise的状态通过defer.reject()变成rejected,控制台输出rejected
var Q = require('q');
var defer = Q.defer();
* 通过defer获得promise
* @private
function getInputPromise() {
return defer.
* 当inputPromise状态由未完成变成fulfil时,调用function(fulfilled)
* 当inputPromise状态由未完成变成rejected时,调用function(rejected)
* 将then返回的promise赋给outputPromise
* function(fulfilled) 和 function(rejected) 通过返回字符串将outputPromise的状态由
* 未完成改变为fulfilled
* @private
var outputPromise = getInputPromise().then(function(fulfilled){
return 'fulfilled';
},function(rejected){
return 'rejected';
* 当outputPromise状态由未完成变成fulfil时,调用function(fulfilled),控制台打印'fulfilled: fulfilled'。
* 当outputPromise状态由未完成变成rejected, 调用function(rejected), 控制台打印'fulfilled: rejected'。
outputPromise.then(function(fulfilled){
console.log('fulfilled: ' + fulfilled);
},function(rejected){
console.log('rejected: ' + rejected);
* 将inputPromise的状态由未完成变成rejected
defer.reject(); //输出 fulfilled: rejected
* 将inputPromise的状态由未完成变成fulfilled
//defer.resolve(); //输出 fulfilled: fulfilled
当function(fulfilled)或者function(rejected)抛出异常时,那么outputPromise的状态就会变成rejected
var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
* 通过defer获得promise
* @private
function getInputPromise() {
return defer.
* 当inputPromise状态由未完成变成fulfil时,调用function(fulfilled)
* 当inputPromise状态由未完成变成rejected时,调用function(rejected)
* 将then返回的promise赋给outputPromise
* function(fulfilled) 和 function(rejected) 通过抛出异常将outputPromise的状态由
* 未完成改变为reject
* @private
var outputPromise = getInputPromise().then(function(fulfilled){
throw new Error('fulfilled');
},function(rejected){
throw new Error('rejected');
* 当outputPromise状态由未完成变成fulfil时,调用function(fulfilled)。
* 当outputPromise状态由未完成变成rejected, 调用function(rejected)。
outputPromise.then(function(fulfilled){
console.log('fulfilled: ' + fulfilled);
},function(rejected){
console.log('rejected: ' + rejected);
* 将inputPromise的状态由未完成变成rejected
defer.reject();
//控制台打印 rejected [Error:rejected]
* 将inputPromise的状态由未完成变成fulfilled
//defer.resolve(); //控制台打印 rejected [Error:fulfilled]
当function(fulfilled)或者function(rejected)返回一个promise时,outputPromise就会成为这个新的promise.
这样做有什么意义呢? 主要在于聚合结果(Q.all),管理延时,异常恢复等等
比如说我们想要读取一个文件的内容,然后把这些内容打印出来。可能会写出这样的代码:
//错误的写法
var outputPromise = getInputPromise().then(function(fulfilled){
fs.readFile('test.txt','utf8',function(err,data){
然而这样写是错误的,因为function(fulfilled)并没有返回任何值。需要下面的方式:
var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
* 通过defer获得promise
* @private
function getInputPromise() {
return defer.
* 当inputPromise状态由未完成变成fulfil时,调用function(fulfilled)
* 当inputPromise状态由未完成变成rejected时,调用function(rejected)
* 将then返回的promise赋给outputPromise
* function(fulfilled)将新的promise赋给outputPromise
* 未完成改变为reject
* @private
var outputPromise = getInputPromise().then(function(fulfilled){
var myDefer = Q.defer();
fs.readFile('test.txt','utf8',function(err,data){
if(!err && data) {
myDefer.resolve(data);
return myDefer.
},function(rejected){
throw new Error('rejected');
* 当outputPromise状态由未完成变成fulfil时,调用function(fulfilled),控制台打印test.txt文件内容。
outputPromise.then(function(fulfilled){
console.log(fulfilled);
},function(rejected){
console.log(rejected);
* 将inputPromise的状态由未完成变成rejected
//defer.reject();
* 将inputPromise的状态由未完成变成fulfilled
defer.resolve(); //控制台打印出 test.txt 的内容
方法传递有些类似于Java中的try和catch。当一个异常没有响应的捕获时,这个异常会接着往下传递。
方法传递的含义是当一个状态没有响应的回调函数,就会沿着then往下找。
没有提供function(rejected)
var outputPromise = getInputPromise().then(function(fulfilled){})
如果inputPromise的状态由未完成变成rejected, 此时对rejected的处理会由outputPromise来完成。
var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
* 通过defer获得promise
* @private
function getInputPromise() {
return defer.
* 当inputPromise状态由未完成变成fulfil时,调用function(fulfilled)
* 当inputPromise状态由未完成变成rejected时,这个rejected会传向outputPromise
var outputPromise = getInputPromise().then(function(fulfilled){
return 'fulfilled'
outputPromise.then(function(fulfilled){
console.log('fulfilled: ' + fulfilled);
},function(rejected){
console.log('rejected: ' + rejected);
* 将inputPromise的状态由未完成变成rejected
defer.reject('inputpromise rejected'); //控制台打印rejected: inputpromise rejected
* 将inputPromise的状态由未完成变成fulfilled
//defer.resolve();
没有提供function(fulfilled)
var outputPromise = getInputPromise().then(null,function(rejected){})
如果inputPromise的状态由未完成变成fulfilled, 此时对fulfil的处理会由outputPromise来完成。
var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
* 通过defer获得promise
* @private
function getInputPromise() {
return defer.
* 当inputPromise状态由未完成变成fulfil时,传递给outputPromise
* 当inputPromise状态由未完成变成rejected时,调用function(rejected)
* function(fulfilled)将新的promise赋给outputPromise
* 未完成改变为reject
* @private
var outputPromise = getInputPromise().then(null,function(rejected){
return 'rejected';
outputPromise.then(function(fulfilled){
console.log('fulfilled: ' + fulfilled);
},function(rejected){
console.log('rejected: ' + rejected);
* 将inputPromise的状态由未完成变成rejected
//defer.reject('inputpromise rejected');
* 将inputPromise的状态由未完成变成fulfilled
defer.resolve('inputpromise fulfilled'); //控制台打印fulfilled: inputpromise fulfilled
可以使用fail(function(error))来专门针对错误处理,而不是使用then(null,function(error))
var outputPromise = getInputPromise().fail(function(error){})
看这个例子
var Q = require('q');
var fs = require('fs');
var defer = Q.defer();
* 通过defer获得promise
* @private
function getInputPromise() {
return defer.
* 当inputPromise状态由未完成变成fulfil时,调用then(function(fulfilled))
* 当inputPromise状态由未完成变成rejected时,调用fail(function(error))
* function(fulfilled)将新的promise赋给outputPromise
* 未完成改变为reject
* @private
var outputPromise = getInputPromise().then(function(fulfilled){
}).fail(function(error){
console.log('fail: ' + error);
* 将inputPromise的状态由未完成变成rejected
defer.reject('inputpromise rejected');//控制台打印fail: inputpromise rejected
* 将inputPromise的状态由未完成变成fulfilled
//defer.resolve('inputpromise fulfilled');
可以使用progress(function(progress))来专门针对进度信息进行处理,而不是使用 then(function(success){},function(error){},function(progress){})
var Q = require('q');
var defer = Q.defer();
* 获取初始promise
* @private
function getInitialPromise() {
return defer.
* 为promise设置progress信息处理函数
var outputPromise = getInitialPromise().then(function(success){
}).progress(function(progress){
console.log(progress);
defer.notify(1);
defer.notify(2); //控制台打印1,2
promise链提供了一种让函数顺序执行的方法。
函数顺序执行是很重要的一个功能。比如知道用户名,需要根据用户名从数据库中找到相应的用户,然后将用户信息传给下一个函数进行处理。
var Q = require('q');
var defer = Q.defer();
//一个模拟数据库
var users = [{'name':'andrew','passwd':'password'}];
function getUsername() {
return defer.
function getUser(username){
users.forEach(function(element){
if(element.name === username) {
//promise链
getUsername().then(function(username){
return getUser(username);
}).then(function(user){
console.log(user);
defer.resolve('andrew');
我们通过两个then达到让函数顺序执行的目的。
then的数量其实是没有限制的。当然,then的数量过多,要手动把他们链接起来是很麻烦的。比如
foo(initialVal).then(bar).then(baz).then(qux)
这时我们需要用代码来动态制造promise链
var funcs = [foo,bar,baz,qux]
var result = Q(initialVal)
funcs.forEach(function(func){
result = result.then(func)
return result
当然,我们可以再简洁一点
var funcs = [foo,bar,baz,qux]
funcs.reduce(function(pre,current),Q(initialVal){
return pre.then(current)
看一个具体的例子
function foo(result) {
console.log(result);
return result+
//手动链接
Q('hello').then(foo).then(foo).then(foo);
//控制台输出: hello
hellohello
hellohellohello
//动态链接
var funcs = [foo,foo,foo];
var result = Q('hello');
funcs.forEach(function(func){
result = result.then(func);
//精简后的动态链接
funcs.reduce(function(prev,current){
return prev.then(current);
},Q('hello'));
对于promise链,最重要的是需要理解为什么这个链能够顺序执行。如果能够理解这点,那么以后自己写promise链可以说是轻车熟路啊。
promise组合
回到我们一开始读取文件内容的例子。如果现在让我们把它改写成promise链,是不是很简单呢?
var Q = require('q'),
fs = require('fs');
function printFileContent(fileName) {
return function(){
var defer = Q.defer();
fs.readFile(fileName,'utf8',function(err,data){
if(!err && data) {
console.log(data);
defer.resolve();
return defer.
//手动链接
printFileContent('sample01.txt')()
.then(printFileContent('sample02.txt'))
.then(printFileContent('sample03.txt'))
.then(printFileContent('sample04.txt'));
//控制台顺序打印sample01到sample04的内容
很有成就感是不是。然而如果仔细分析,我们会发现为什么要他们顺序执行呢,如果他们能够并行执行不是更好吗? 我们只需要在他们都执行完成之后,得到他们的执行结果就可以了。
我们可以通过Q.all([promise1,promise2...])将多个promise组合成一个promise返回。
当all里面所有的promise都fulfil时,Q.all返回的promise状态变成fulfil
当任意一个promise被reject时,Q.all返回的promise状态立即变成reject
我们来把上面读取文件内容的例子改成并行执行吧
var Q = require('q');
var fs = require('fs');
*读取文件内容
function printFileContent(fileName) {
//Todo: 这段代码不够简洁。可以使用Q.denodeify来简化
var defer = Q.defer();
fs.readFile(fileName,'utf8',function(err,data){
if(!err && data) {
console.log(data);
defer.resolve(fileName + ' success ');
defer.reject(fileName + ' fail ');
return defer.
Q.all([printFileContent('sample01.txt'),printFileContent('sample02.txt'),printFileContent('sample03.txt'),printFileContent('sample04.txt')])
.then(function(success){
console.log(success);
}); //控制台打印各个文件内容 顺序不一定
现在知道Q.all会在任意一个promise进入reject状态后立即进入reject状态。如果我们需要等到所有的promise都发生状态后(有的fulfil, 有的reject),再转换Q.all的状态, 这时我们可以使用Q.allSettled
var Q = require('q'),
fs = require('fs');
*读取文件内容
function printFileContent(fileName) {
//Todo: 这段代码不够简洁。可以使用Q.denodeify来简化
var defer = Q.defer();
fs.readFile(fileName,'utf8',function(err,data){
if(!err && data) {
console.log(data);
defer.resolve(fileName + ' success ');
defer.reject(fileName + ' fail ');
return defer.
Q.allSettled([printFileContent('nosuchfile.txt'),printFileContent('sample02.txt'),printFileContent('sample03.txt'),printFileContent('sample04.txt')])
.then(function(results){
results.forEach(
function(result) {
console.log(result.state);
结束promise链
通常,对于一个promise链,有两种结束的方式。第一种方式是返回最后一个promise
如 return foo().then(bar);
第二种方式就是通过done来结束promise链
如 foo().then(bar).done()
为什么需要通过done来结束一个promise链呢? 如果在我们的链中有错误没有被处理,那么在一个正确结束的promise链中,这个没被处理的错误会通过异常抛出。
var Q = require('q');
function getPromise(msg,timeout,opt) {
var defer = Q.defer();
setTimeout(function(){
console.log(msg);
defer.reject(msg);
defer.resolve(msg);
},timeout);
return defer.
*没有用done()结束的promise链
*由于getPromse('2',2000,'opt')返回rejected, getPromise('3',1000)就没有执行
*然后这个异常并没有任何提醒,是一个潜在的bug
getPromise('1',3000)
.then(function(){return getPromise('2',2000,'opt')})
.then(function(){return getPromise('3',1000)});
*用done()结束的promise链
*有异常抛出
getPromise('1',3000)
.then(function(){return getPromise('2',2000,'opt')})
.then(function(){return getPromise('3',1000)})
当你理解完上面所有的知识点时,你就会正确高效的使用promise了。本节只是讲了promise的原理和几个基本的API,不过你掌握了这些之后,再去看q的文档,应该很容易就能理解各个api的意图。

我要回帖

更多关于 百度地图api回调顺序 的文章

 

随机推荐