用于处理成功、失败、重试的异步请求的设计模式?(JavaScript)

A design pattern for async requests to handle success, failure, retry ? (javascript)

本文关键字:设计模式 JavaScript 请求 异步 处理 失败 重试 用于 成功      更新时间:2023-09-26

我正在使用Appcelerator Titanium编写一个移动应用程序,该应用程序发出许多不同的xhr请求。这不是一个真正的Appcelerator Titanium特定问题。但是如果你确实写了一些代码,我希望它是javascript。

应用需要对自身进行身份验证,必须记录用户才能进行某些交互等。

我已经到了任何请求都可能得到任何类型的响应的地步,例如:

  • 未通过身份验证
  • 未记录
  • 坏参数
  • 成功的

请求包装在不同的模型方法或帮助程序中。

问题是,我不熟悉这种应用程序。我想知道什么是最佳实践。

例如,一些真正的问题是:

  • 如果应用未通过身份验证(令牌已过期,首次启动),应用是否应尝试对自身进行身份验证,然后再次发送被拒绝的请求?(对用户透明)

  • 我是否应该在每次应用程序启动时发送身份验证请求,然后"忘记"它?

我面临的问题是,如果我尝试为每个请求处理这个问题,代码很快就会变大。充满嵌套回调、重试条件、要管理的各种事件侦听器等。只是感觉不是很"好"。而且它根本不是 DRY,当我真正需要的是任何请求时,检查出了什么问题,尝试修复它(如果没有,则进行身份验证,如果可能的话自动登录或显示登录 UI 等),然后如果有效,请重试原始请求几次,如果需要,请中止。

我一直在研究承诺模式,但只知道理论,不知道它是否是我需要的。

因此,我欢迎有关此特定问题的任何建议。我想知道像"Facebook"这样的应用程序如何处理这个问题。

谢谢你的帮助

这个问题不容易回答,但让我试着给你一些想法:

在对应用进行任何编码之前,最重要的是 API 本身。它必须可靠并符合标准。我不会在这里详细介绍,但是一个编写良好的RESTful API可以显着降低httpClient的复杂性。它必须使用标准的http状态代码和诸如POST,GET,PUT,DELETE...

一个很好的读物是George Reese的The REST API设计手册。

我使用 Titanium 的 httpClients 的方法是单个模块,它在需要时通过 require() 加载。我一次坚持使用一个客户端,因为我在多个并行调用时遇到了大量问题。每当进行调用时,客户端都会检查是否已有正在进行的调用,并在必要时将其发送到队列。

我给大家举个例子。为了简洁起见,我省略了很多东西:

// lib/customClient.js
var xhrRequest;     // This will be our HTTPClient
var callQueue = []; // This will be our queue
// Register the request
// params are:
// method (e.g. 'GET')
// url (e.g. 'http://test.com/api/v1/user/1')
// done (callback function)
function registerRequest(params) {
    if(!xhrRequest) {
        sendRequest(params);
    } else {
        queueRequest(params);
    }
}
// This simply sends the request
// to the callQueue
function queueRequest(params) {
    callQueue.push(params);
}
// Send the request with the params from register
// Please note that I do not hardcode error messages,
// I just do it here so it is easier to read
function sendRequest(params) {
    // Set callback if available and valid
    var callback = params.done && typeof(params.done) === "function" ? params.callback : null;
    // Set method
    var method = params.method || 'GET';
    // Create the HTTP Client
    xhrRequest = Ti.Network.createHTTPClient({
        // Success
        onload: function() {
            // You can check for status codes in detail here
            // For brevity, I will just check if it is valid
            if (this.status >= 200 && this.status < 300) {
                if(this.responseText) {
                    // You might want to check if it can be parsed as JSON here
                    try {
                        var jsonData = JSON.parse(this.responseText);
                        if(callback) callback({ success: true, response: jsonData });
                    } catch(e) {
                        if(callback) callback({ success: false, errormessage: 'Could not parse JSON data' });
                    }
                    processQueue();
                } else {
                    if(callback) callback({ success: false, errormessage: 'No valid response received' });
                    processQueue();
                }
            } else {
                if(callback) callback({ success: false, errormessage: 'Call response is success but status is ' + this.status });
                processQueue();
            }
        },
        // Error
        onerror: function(e) {
            if(this.responseText) {
                try {
                    var jsonData = JSON.parse(this.responseText);
                    if(callback) callback({ success: false, reponse: jsonData });
                } catch(e) {};
            }
            processQueue();
        },
    });
    // Prepare and send request
    // A lot more can (and should) be configured here, check documentation!
    xhrRequest.setTimeout(10000);
    xhrRequest.open(method, params.url);
    xhrRequest.send();
}
// Checks if there is anything else in the queue
// and sends it
function processQueue() {
    xhrRequest = null;
    var nextInQueue = callQueue.shift();
    if(nextInQueue) sendRequest(nextInQueue);
}
// Our public API
var publicAPI = {
    sendRequest: function(params) {
        registerRequest(params);
    }
};
module.exports = publicAPI;

然后,我可以从任何其他控制器/视图发送呼叫

var customClient = require('lib/customClient'); // omit 'lib' if you use alloy
// Send the request
customClient.sendRequest({
        method        : 'GET',
        url           : 'http://test.com/api/v1/user/1',
        done          : function(response) {
                           Ti.API.debug(JSON.stringify(response));
                        }
});

请注意,这不完整,不会检查连接性,没有真正的错误处理等,但它可能会帮助您了解一些想法。

我认为这里有很多东西要谈,但我现在就到此为止......