从 AJAX 调用异步 Servlet

3
我想要实现的事情并不太复杂,但由于我不熟悉AJAX,所以有些困难。
在实现时,我会有一个JSP页面,其中包含一个按钮,点击按钮后会调用异步Servlet。该Servlet会执行一个长时间运行的任务,并通过在任务完成部分时向表格添加行来向用户提供动态反馈。
在尝试编写最终版之前,我正在进行概念验证,以了解它的工作原理。但是,我遇到了一些问题。当我使用AJAX调用来点击按钮时,如果该调用指向常规同步Servlet,则函数按预期工作。但是,一旦我将servlet设置为异步,更新就不会显示。
是否有人能为我提供一些出了什么问题的见解?
我的JSP页面如下:
<html>
    <body>
        <script type="text/javascript" charset="utf-8">
            $(document).ready(function() {
                $('#mybutton').click(function() {
                    $.get('someservlet', function(responseJson) {
                        $.each(responseJson, function(index, item) {
                            $('<ul>').appendTo('#somediv');
                            $('<li>').text(item.row1).appendTo('#somediv');
                            $('<li>').text(item.row2).appendTo('#somediv');
                            $('<li>').text(item.row3).appendTo('#somediv');
                            $('<li>').text(item.row4).appendTo('#somediv');
                        });
                    });
                });
            });
        </script>
        <p><button id="mybutton">Click to add things</button></p>
        <div id="somediv"></div>
    </body>
</html>

我的异步Servlet的doGet()方法如下所示:
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
final AsyncContext asyncContext = request.startAsync();
final PrintWriter writer = response.getWriter();
asyncContext.setTimeout(10000);
asyncContext.start(new Runnable() {

@Override
public void run() {
    for (int i = 0; i < 10; i++) {
        List<Row> rows = new ArrayList<Row>();
        rows.add(new Row(i, i + 1, i + 2, i + 3));
        String json = new Gson().toJson(rows);
        writer.write(json);
        writer.flush();
        log.info("Wrote to JSON: " + i);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
        }
    }
    asyncContext.complete();
    }
});

有什么想法吗?当我点击按钮时,似乎只能接受来自主 servlet 线程的 AJAX 响应。也许我需要从异步write()调用中调用 JavaScript 函数?我不确定如何做这件事或者是否这样执行是正确的。

2个回答


0

好的!

所以,我想出来了。它不如我想象的那么花哨,但它有效。而且,这只是我正在工作的实现概念证明。如果有人可以提供进一步的见解,我很乐意使用服务器推送而不是轮询来实现这个。

你有什么想法?

我的JSP看起来像这样:

<html>
    <head>
        <script src="jquery-1.7.1.min.js" type="text/javascript" ></script>
        <script>
            $(document).ready(function() {
                var prevDataLength;
                var nextLine = 0;
                var pollTimer;
                $('#abutton').click(function() {
                    $(function(){
                        var x = new $.ajaxSettings.xhr();
                        x.open("POST", "someservlet");
                        handleResponseCallback = function(){
                            handleResponse(x);
                        };
                        x.onreadystatechange = handleResponseCallback;
                        pollTimer = setInterval(handleResponseCallback, 100);
                        x.send(null);
                    });
                });

                function handleResponse(http) {
                    if (http.readyState != 4 && http.readyState != 3)
                        return;
                    if (http.readyState == 3 && http.status != 200)
                        return;
                    if (http.readyState == 4 && http.status != 200) {
                        clearInterval(pollTimer);
                    }

                    while (prevDataLength != http.responseText.length) {
                        if (http.readyState == 4  && prevDataLength == http.responseText.length)
                            break;
                        prevDataLength = http.responseText.length;
                        var response = http.responseText.substring(nextLine);
                        var lines = response.split('\n');
                        nextLine = nextLine + response.lastIndexOf(']') + 1;
                        if (response[response.length-1] != ']')
                            lines.pop();
                        for (var i = 0; i < lines.length; i++) {
                            var line = $.parseJSON(lines[i]);
                            addToTable(line);
                        }
                    }

                    if (http.readyState == 4 && prevDataLength == http.responseText.length)
                        clearInterval(pollTimer);
                }

                function addToTable(JSONitem) {
                    $.each(JSONitem, function(index, item) {
                        $('<tr>').appendTo('#sometablebody')
                        .append($('<td>').text(item.name))
                        .append($('<td>').text(item.message))
                        .append($('<td>').text(item.number))
                        .append($('<td>').append($('<a>').attr('href', item.link).text('link')));
                    });

                }
            });
        </script>
        <title>Async Test</title>
    </head>
    <body>
        <p><button id="abutton">Click to add things</button></p>
        <div id="somediv">
            <table border="1">
                <thead>
                    <tr>
                        <td>Name</td>
                        <td>Message</td>
                        <td>Number</td>
                        <td>Link</td>
                    </tr>
                </thead>
                <tbody id="sometablebody"></tbody>
            </table>
        </div>
    </body>
</html>

我的异步Servlet doGet() 方法如下:

request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

        final AsyncContext asyncContext = request.startAsync();
        final PrintWriter writer = response.getWriter();
        asyncContext.setTimeout(60000);
        asyncContext.start(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        List<Row> list = new ArrayList<Row>();
                        list.add(new Row("First", "This is the first", String.valueOf(i), "link" + i));
                        list.add(new Row("Second", "This is the second", String.valueOf(i), "link" + i));
                        list.add(new Row("Third", "This is the third", String.valueOf(i), "link" + i));
                        String json = new Gson().toJson(list);

                        asyncContext.getResponse().setContentType("application/json");
                        asyncContext.getResponse().setCharacterEncoding("UTF-8");
                        try {
                            asyncContext.getResponse().getWriter().write(json);
                            asyncContext.getResponse().getWriter().flush();
                        } catch (IOException ex) {
                            System.out.println("fail");
                        }

                        Thread.sleep(250);
                    } catch (InterruptedException ex) {
                        break;
                    }
                }
                asyncContext.complete();
            }
        });

此外,为了使所有这些工作正常运行,我实现了一个简单的Row类:
public class Row {

    private String name;
    private String message;
    private String number;
    private String link;

    public Row(String name, String message, String number, String link) {
        setName(name);
        setMessage(message);
        setNumber(number);
        setLink(link);
    }

    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }
}

1
那么问题出在哪里呢?就是这一行代码吗:request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true); 如果服务器不支持异步,应该抛出异常。 - dragon66
当我在本地从 NetBeans 运行时,会出现 NetBeans 使用的筛选器存在问题。我必须设置此标志才能使其正常工作。但是在实际部署时,它可以在没有此标志的情况下正常工作。 - Zach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接