$.when().done().then()的用法以及jQuery的deferred对象详解

本文深入解析jQuery中Deferred对象及其$.when()方法的使用,通过实例代码演示如何处理异步操作,如AJAX请求,并展示如何通过deferred对象解决jQuery回调函数的局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

讲解详细地址:$.when().done().then()的用法

开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。

通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。

但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象

简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是"延迟",所以deferred对象的含义就是"延迟"到未来某个点再执行

 范例:JeeSite Demo项目jssSite.js代码片段

deferAjax: function ( url, data, message ) {   //jssSite.js代码中定义了名称为js的对象,此为js对象的一方法
      var defer = $.Deferred();                        //$.when()的参数只能是deferred对象
      $.ajax( {
        url: url,
        type: 'POST',
        data: data,
        dataType: 'json',
        beforeSend: function () {                 
          js.loading( message == undefined ? js.text( "loading.submitMessage" ) : message );
          $( ".btn" ).attr( "disabled", true );
        },
        complete: function () {
          $( ".btn" ).attr( "disabled", false );
          js.closeLoading();
        },
        success: function ( ret ) {
          defer.resolve( ret );
        },
        error: function (ret ) {
          $( ".btn" ).attr( "disabled", false );
          js.showErrorMessage( ret.responseText );
          js.closeLoading( 0, true )
        }
      } );
      return defer;
    }

 //beforeSend  complete  success  error  的使用见:https://blog.youkuaiyun.com/xiangwang2016/article/details/87261879


$.when( js.deferAjax( url, data, "添加菜单中...." ) ).done( function ( ret ) {     //js对象调取deferAjax()方法
              console.log( JSON.stringify( ret ) );
              if ( ret.code == "0000" ) {
                js.showMessage( ret.result, "添加状态", "success", "1000" );
                js.closeCurrentTabPage( function ( contentWindow ) {
                  contentWindow.$( '#dataGrid' ).dataGrid( 'refreshTreeChildren' );
                } );
              } else {
                js.showErrorMessage( ret.message );
              }
  } );

<think>我们正在讨论SignalR的连接启动方法链式调用的用法。用户提到了引用[1]是关于在Startup中注册SignalR中间件,但用户的问题是关于客户端连接启动的链式调用。根据用户的问题,他想要了解SignalR连接启动时的链式调用用法,特别是关于`.Start()`、`.Done()`、`.Always()`等方法的使用示例。在SignalR中,客户端连接(例如JavaScript客户端)的启动过程通常是这样的:1.创建连接对象:`varconnection=newsignalR.HubConnection(url);`2.可以设置一些事件处理程序(例如接收消息、连接状态变化等)3.启动连接:`connection.start()`在早期版本的SignalR(比如1.x)中,有`.done()`和`.always()`等方法,它们类似于jQueryDeferred对象,用于处理异步操作的回调。但是,请注意,在较新版本的SignalR(例如ASP.NETCoreSignalR)中,API已经发生了变化。`.start()`方法返回的是一个Promise(在JavaScript中),因此我们可以使用`.then()`和`.catch()`,或者使用async/await。不过,为了回答用户的问题,我们先展示旧版(SignalRforASP.NET)的用法,然后再展示新版(ASP.NETCoreSignalR)的用法。由于用户的问题中没有明确指定是哪个版本,我们尽量提供两种版本的示例。用户的问题中提到了“链式调用”,所以我们重点展示如何链式调用这些方法。引用[1]是关于服务端配置的,与我们当前问题不是直接相关。引用[2]是关于内核跟踪的,完全无关。因此我们忽略引用[2],而引用[1]只说明用户可能已经配置了SignalR服务端。我们主要参考用户上一次的问题(关于SignalR的连接启动方法链式调用)来回答。下面我将提供两个示例:一个是旧版SignalR,另一个是ASP.NETCoreSignalR(新版)。旧版SignalR(jQuery风格):connection.start().done(function(){console.log('连接建立成功');}).fail(function(){console.log('连接建立失败');}).always(function(){console.log('无论成功或失败都会执行');});在新版ASP.NETCoreSignalR中,由于API设计遵循Promise规范,我们可以这样写:connection.start().then(()=>{console.log('连接建立成功');}).catch(err=>{console.error('连接建立失败:',err);});注意:新版中没有`.always()`方法,但是我们可以使用Promise的finally方法(需要确保环境支持Promise.prototype.finally)或者自己模拟。例如:connection.start().then(()=>{console.log('连接建立成功');}).catch(err=>{console.error('连接建立失败:',err);}).finally(()=>{console.log('无论成功或失败都会执行');});如果环境不支持finally,可以用以下方式:functionalways(callback){returnthis.then(callback,callback);}//然后扩展Promiseif(!Promise.prototype.always){Promise.prototype.always=always;}//但这样扩展原生对象并不推荐,所以更建议使用一个辅助函数来处理。在实际使用中,建议使用async/await语法,这样代码更清晰。try{awaitconnection.start();console.log('连接建立成功');}catch(err){console.error('连接建立失败:',err);}finally{console.log('无论成功或失败都会执行');}由于用户问题中的示例使用了“done”、“always”等,很可能是旧版,但考虑到现在大多使用新版,我们提供两种。现在,我们按照用户要求,提供链式调用的示例代码。注意:用户的问题中提到了“hubstartdonealways”,所以我们要包含这些关键字。我们使用JavaScript客户端示例。示例1:旧版SignalR(假设使用jQuery.signalR)varconnection=$.hubConnection('/signalr');//注意:旧版使用$.hubConnectionvarhubProxy=connection.createHubProxy('chatHub');hubProxy.on('broadcastMessage',function(name,message){console.log(name+''+message);});connection.start().done(function(){console.log('现在已连接,连接ID='+connection.id);}).fail(function(){console.log('连接失败');}).always(function(){console.log('连接过程完成(无论成功或失败)');});示例2:新版ASP.NETCoreSignalR(JavaScript客户端)://创建连接constconnection=newsignalR.HubConnectionBuilder().withUrl("/chatHub").build();//注册接收消息的处理程序connection.on("ReceiveMessage",(user,message)=>{console.log(user+':'+message);});//启动连接并处理回调connection.start().then(()=>{console.log('连接成功,连接ID:'+connection.connectionId);}).catch(err=>{console.error('连接失败:',err);})//如果支持finally则用finally,否则我们需要自己处理.finally(()=>{console.log('连接尝试完成(无论成功或失败)');});注意:在新版中,连接ID并不是在连接成功后自动就有(实际上connection.connectionId在新版中是在连接建立后由服务端分配,但是在连接成功之前是null)。所以我们在then里面使用它。如果用户的运行环境不支持finally,但又不想扩展Promise,可以这样写:letstarted=connection.start();started.then(()=>{console.log('连接成功');},(err)=>{console.error('连接失败:',err);});started.then(()=>{console.log('连接过程完成(无论成功或失败)');},()=>{console.log('连接过程完成(无论成功或失败)');});但这样写比较冗余。更好的方式是用一个辅助函数:functionalways(promise,callback){returnpromise.then((result)=>{callback();returnresult;},(error)=>{callback();throwerror;});}always(connection.start(),()=>{console.log('连接过程完成(无论成功或失败)');}).then(()=>{console.log('连接成功');}).catch(err=>{console.error('连接失败:',err);});不过,在支持finally的环境中,直接用finally即可。总结:-旧版SignalR:使用`.done()`,`.fail()`,`.always()`进行链式调用。-新版SignalR:使用`.then()`,`.catch()`,`.finally()`(或使用async/await)来处理连接结果。如果用户的环境不支持Promise.prototype.finally,可以使用polyfill,或者使用上面提到的辅助方法。由于问题中明确要求“链式调用”,我们以链式调用的方式展示。最后,根据用户要求,生成相关问题。</think> ### SignalR 连接启动方法链式调用的用法详解 在 SignalR 中,客户端连接的启动过程支持链式调用(Method Chaining),通过 `.start()`、`.done()`、`.fail()`、`.always()` 等方法实现异步操作的顺序执行和状态管理。以下是具体用法和示例: #### 🔗 1. **基础链式调用结构** ```javascript const connection = new signalR.HubConnectionBuilder() .withUrl("/notificationHub") // 对应服务端注册的端点 .build(); connection.start() .done(() => { /* 连接成功回调 */ }) .fail((err) => { /* 连接失败回调 */ }) .always(() => { /* 终态逻辑 */ }); ``` - **`.start()`**:启动连接(返回 Promise 对象) - **`.done(func)`**:连接成功时触发的回调 - **`.fail(func)`**:连接失败时触发的回调(参数为错误对象) - **`.always(func)`**:无论成功或失败都会触发的回调 #### 🧪 2. **完整示例(JavaScript 客户端)** ```javascript // 1. 创建连接对象 const connection = new signalR.HubConnectionBuilder() .withUrl("/notifyHub") // 匹配服务端映射的端点[^1] .configureLogging(signalR.LogLevel.Information) .build(); // 2. 定义消息接收处理 connection.on("ReceiveNotification", (message) => { console.log("收到推送:", message); }); // 3. 链式启动连接 connection.start() .done(() => { console.log("连接已建立,连接ID:", connection.connectionId); connection.invoke("SubscribeToUpdates"); // 调用服务端方法 }) .fail((error) => { console.error("连接失败:", error); alert("实时推送服务不可用!"); }) .always(() => { console.log("连接启动流程结束"); // 此处可执行清理或状态重置逻辑 }); ``` #### ⚙️ 3. **关键特性说明** 1. **异步顺序执行**: `.start().done(...).fail(...)` 保证执行顺序: - 先尝试建立连接 - 成功则执行 `.done()` - 失败则执行 `.fail()` - 最终必执行 `.always()` 2. **`always()` 的典型场景**: - 隐藏连接加载动画 - 重置 UI 按钮状态 - 记录连接终态日志 ```javascript .always(() => { document.getElementById("loading-spinner").hidden = true; }); ``` 3. **与 async/await 的等价写法**: 链式调用等同于: ```javascript try { await connection.start(); // .done() 内容 } catch (err) { // .fail() 内容 } finally { // .always() 内容 } ``` #### ⚠️ 4. **注意事项** - **版本兼容性**: 链式调用语法适用于 **SignalR for ASP.NET Core**(`@microsoft/signalr` npm 包) - **服务端要求**: 需在服务端正确配置 Hub 终结点(如 `MapHub<NotificationHub>("/notifyHub")`[^1]) - **错误处理**: 务必实现 `.fail()` 避免未捕获的 Promise 异常 --- ### 🔍 相关问题 1. SignalR 客户端如何实现自动重连机制? 2. 如何在连接断开时触发特定的 UI 状态更新? 3. SignalR 的 `.invoke()` 和 `.send()` 方法有什么区别? 4. 如何限制 SignalR 连接的最大并发数? [^1]: 需在服务端 Startup 中配置 `MapHub<NotificationHub>("/notifyHub")` [^2]: 调试时可开启日志追踪(`configureLogging`),生产环境建议关闭
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值