代码之家  ›  专栏  ›  技术社区  ›  Darrell Brogdon

是否有任何理由使用同步XMLHttpRequest?

  •  83
  • Darrell Brogdon  · 技术社区  · 16 年前

    似乎大多数人都使用XMLHttpRequest执行异步请求,但显然,能够执行同步请求这一事实表明,这样做可能是有道理的。那么,这可能是什么原因呢?

    18 回复  |  直到 16 年前
        1
  •  26
  •   Haroldo_OK    8 年前

    我认为随着HTML5标准的发展,它们可能会变得更加流行。如果web应用程序能够访问web worker,我可以预见开发人员将使用专用的web worker来同步请求,正如Jonathan所说,以确保一个请求先于另一个请求。在当前只有一个线程的情况下,这是一个不太理想的设计,因为它会阻塞直到请求完成。

        2
  •  32
  •   Frank Krueger    15 年前

    beforeunload 事件您可以在用户关闭页面时将数据上载到服务器。

        3
  •  16
  •   Community Mohan Dere    6 年前

    更新:

    尽管这听起来很糟糕,但有时在用户离开页面之前或在执行操作之前发生请求(或一系列请求)很重要-阻止其他代码执行(例如,阻止后退按钮)可能会减少用户的错误/维护 设计系统;也就是说,我从来没有在世界上见过它 野生的

    promise ,通过回调链接进程来假装同步性。这适合大多数开发需求,其中的目标是有序、无阻塞的事件,使浏览器能够为用户保留响应能力(良好的用户体验)。

    作为 stated in the Mozilla docs 在某些情况下,您必须使用同步请求;但是,还列出了一个使用 (IE/Safari中不提供)用于此类情况。虽然这是实验性的,但如果它能达到标准接受度,它可能会在同步请求棺材中钉上钉子。


    您可能希望在任何类型的事务(如处理)中执行同步调用,或者在任何需要操作顺序的地方执行同步调用。

    例如,假设您希望自定义一个事件,以便在播放歌曲后注销。如果先进行注销操作,则永远不会播放歌曲。这需要同步请求。

    例子: 服务器有一个值为1的变量。

     Step (1) Perform Update: add 1 to variable
     Step (2) Perform Update: set variable to the power of 3
     End Value: variable equals 8
    


    在一个常见的现实世界示例中,同步调用很少被证明是合理的。可能是在单击“登录”,然后单击站点中需要用户登录的部分时。

    正如其他人所说,它会束缚你的浏览器,所以尽可能远离它。

    但是,用户通常希望停止当前正在加载的事件,然后执行其他操作,而不是同步调用。在某种程度上,这就是同步,因为第一个事件在第二个事件开始之前退出。为此,对xml连接对象使用abort()方法。

        4
  •  8
  •   hobodave    16 年前

    我会说,如果您考虑在请求完成时阻塞用户的浏览器,那么一定要使用同步请求。

    如果请求序列化是您的目标,那么这可以通过使用异步请求来实现,方法是让上一个请求的onComplete回调触发下一个请求。

        5
  •  8
  •   Alexandre Avrane    11 年前

    以具有多个字段的应用程序为例,某些字段必须通过对远程服务器的xmlhttp调用进行验证,远程服务器提供该字段的值和其他字段的值作为输入。

    在同步模式下,逻辑简单,用户经历的阻塞非常短,没有问题。

    基本上,问题是:什么是更重要的非阻塞UI体验或数据损坏风险?答案应该留给应用程序开发人员,而不是W3C。

        6
  •  6
  •   hyponiq    16 年前

    我可以看到,当变量位置中的一个资源必须在页面中依赖于第一个资源才能完全运行的其他静态资源之前加载时,可以使用同步XHR请求。事实上,我在自己的一个小子项目中实现了这样一个XHR请求,而JavaScript资源根据一组特定参数驻留在服务器上的不同位置。后续JavaScript资源依赖于这些可变资源,必须保证在加载其他依赖文件之前加载这些文件,从而使应用程序完整。

    这个想法的基础真的扩大了对Vo7Ron的回答。基于事务的过程实际上是唯一应该发出同步请求的时间。在大多数其他情况下,异步调用是更好的选择,在调用之后,DOM会根据需要进行更新。在许多情况下,例如基于用户的系统,您可能会将某些功能锁定给“未经授权的用户”,直到他们登录为止。异步调用之后,这些特性将通过DOM更新过程解锁。

        7
  •  4
  •   Henry    14 年前

    在某些情况下,jQuery在内部使用同步AJAX。插入包含脚本的HTML时,浏览器不会执行它们。这些脚本需要手动执行。这些脚本可以附加单击处理程序。假设用户在附加处理程序之前单击某个元素,页面将无法正常工作。因此,为了防止竞争条件,将使用同步AJAX获取这些脚本。因为同步AJAX有效地阻止了其他一切,所以可以确保脚本和事件以正确的顺序执行。

        8
  •  4
  •   jahu    10 年前

        9
  •  3
  •   Jesse Gordon    12 年前

    原因:

    假设您有一个ajax应用程序,在用户进行任何交互之前,该应用程序需要进行六次http访问才能从服务器加载各种数据。

    显然,您希望从onload触发此操作。

    同步调用可以很好地实现这一点,而不会增加代码的复杂性。它简单明了。

    唯一的缺点是,在加载所有数据或发生超时之前,浏览器会锁定。至于讨论中的ajax应用程序,这不是什么大问题,因为在加载所有初始数据之前,应用程序都没有用处。

    可供替代的

    然而,看起来同步GET已经从最近的浏览器中删除或限制了。我不确定这是因为有人认为他们总是不好,还是浏览器作者被WC关于这个主题的工作草案搞糊涂了。

    http://www.w3.org/TR/2012/WD-XMLHttpRequest-20120117/#the-open-method

    我的观点是,阻止IO在某些情况下起着至关重要的作用,但必须正确实施。虽然一个浏览器选项卡或窗口锁定所有其他选项卡或窗口是不可接受的,但这是一个浏览器设计缺陷。羞耻是应得的。但在某些情况下,单个选项卡或窗口在几秒钟内没有响应(即使用阻塞IO/HTTP GET)是完全可以接受的。在某些情况下,例如,在页面加载时,可能需要大量数据才能执行任何操作。有时,正确实现的阻塞代码是最干净的方法。

    当然,在这种情况下,可以使用异步http gets获得等效函数,但需要哪种类型的goofy例程?

    加载文档时,请执行以下操作: 2:执行所有6个后台获取(假设顺序无关紧要)

    然后,6个http get中的每一个的完成回调将设置各自的“完成”标志。 此外,每个回调将检查所有其他完成标志,以查看是否所有6个HTTP GET都已完成。最后一个要完成的回调在看到所有其他回调都已完成后,将调用REAL init函数,该函数将设置所有内容,现在数据已全部获取。

    在它的回调中,将启动第二个回调。 在它的回调中,第三个——以此类推,每次回调都会启动下一个HTTP GET。当最后一个返回时,它将调用真正的init()例程。

        10
  •  2
  •   martinr    16 年前

    如果在生产代码中进行同步调用,会发生什么?

    天塌下来了。

    不,说真的,用户不喜欢锁定的浏览器。

        11
  •  2
  •   Andrea    16 年前

    我知道异步执行会更好,但是我应该为这个特定的验证规则使用不同的代码。我解释得更好。我的验证设置使用一些验证函数,这些函数返回true或false,具体取决于数据是否有效。

    因为函数必须返回,所以我不能使用异步技术,所以我只是使其同步,并希望服务器能够及时响应,不要太引人注目。如果我使用AJAX回调,那么我必须以不同于其他验证方法的方式处理其余的执行。

        12
  •  2
  •   GmonC    16 年前

    有时你的行为取决于别人。例如,操作B只能在A完成时启动。通常使用同步方法来避免 race conditions . 有时,使用同步调用比创建复杂逻辑来检查异步调用中相互依赖的每个状态更简单。

    这种方法的问题是,在操作完成之前(在请求返回、完成、加载等之前),您会“阻止”用户的浏览器。所以使用时要小心。

        13
  •  2
  •   kennebec    16 年前

    当它工作时,我使它异步,但我尝试包括一个中止计时器和失败回调,因为你永远不知道。。。

        14
  •  2
  •   Stijn de Witt    11 年前

    同步与异步:有什么区别?

    基本上可以归结为:

    console.info('Hello, World!');
    doSomething(function handleResult(result) {
        console.info('Got result!');
    });
    console.info('Goodbye cruel world!');
    

    什么时候 doSomething 是 同步的 这将打印:

    Hello, World!
    Got result!
    Goodbye cruel world!
    

    是 异步的

    Hello, World!
    Goodbye cruel world!
    Got result!
    

    因为功能 在异步执行工作时,它会在工作完成之前返回。所以我们只有在打印后才能得到结果 Goodbye cruel world!

    如果我们依赖于异步调用的结果,则需要将依赖的代码放入回调中:

    console.info('Hello, World!');
    doSomething(function handleResult(result) {
        console.info('Got result!');
        if (result === 'good') {
            console.info('I feel great!');
        }
        else {
            console.info('Goodbye cruel world!');
        }
    });
    

    因此,仅仅是2到3件事情需要按顺序发生并不是同步进行的理由(尽管同步代码对于大多数人来说更容易使用)。

    在某些情况下,您需要在被调用函数完成之前得到结果。考虑一下这种情况:

    function lives(name) {
        return (name !== 'Elvis');
    }
    console.info('Elvis ' + (lives('Elvis') ? 'lives!' : 'has left the building...');
    

    假设我们无法控制调用代码( console.info 行)并需要更改功能 lives 生活 还有我们之前的回应 生活 完成。所以我们不知道是否要回去 true false . 在函数完成之前获得结果的唯一方法是执行同步请求。

    Sami Samhuri 在他的回答中提到,在一个非常真实的场景中,在功能终止之前,您可能需要服务器请求的答案是 onbeforeunload 事件,因为它是应用程序中在窗口关闭之前运行的最后一个函数。

    我不需要同步呼叫,但我还是使用它们,因为它们更容易

    请不要。同步呼叫会锁定浏览器,使应用程序感觉无响应。但你是对的。异步代码更难。然而,有一种方法可以使处理它变得更容易。不像同步代码那么简单,但它越来越接近: Promise

    下面是一个示例:在运行第三段代码之前,两个异步调用都应成功完成:

    var carRented = rentCar().then(function(car){
      gasStation.refuel(car);
    });
    
    var hotelBooked = bookHotel().then(function(reservation) {
      reservation.confirm();
    });
    
    Promise.all([carRented, hotelBooked]).then(function(){
      // At this point our car is rented and our hotel booked.
      goOnHoliday();
    });
    

    下面是您将如何实现 bookHotel :

    function bookHotel() {
      return new Promise(function(resolve, reject){
        if (roomsAvailable()) {
          var reservation = reserveRoom();
          resolve(reservation);
        }
        else {
          reject(new Error('Could not book a reservation. No rooms available.'));
        }
      });
    }
    

    另见: Write Better JavaScript with Promises .

        15
  •  2
  •   Mr. Polywhirl    10 年前

    XMLHttpRequest

    您希望在不更改JS代码中所有内容的情况下完成此操作。async/sync标志为您提供了这种能力,如果设计正确,您只需更改代码中的一行/更改一行的值 var 在执行期间。

        16
  •  1
  •   Community Mohan Dere    9 年前

    1. Stackoverflow discussion
    2. Mozilla Firefox XMLHttpRequest

    HTML5 WebWorkers 确实支持超时。因此,您可能希望将sync XHR请求包装为具有超时的WebWorker,以实现具有超时行为的类似异步的XHR。

        17
  •  1
  •   adr    12 年前

    我刚刚遇到过这样一种情况,对使用forEach(和for循环)连续调用的url列表的异步请求会导致其余请求被取消。我切换到同步,它们按预期工作。

        18
  •  1
  •   kockburn    6 年前

    开发应用程序的公司(又名“出版商”)通常会发布广告以赚取收入。为此,他们在应用程序中安装了广告SDK。许多存在(MoPub、Ogury、TapJob、AppNext、谷歌广告AdMob)。

    这些SDK将在网络视图中提供广告。

    当向用户提供广告时,它必须是 平滑 经验 播放视频时。任何时候都不应该有缓冲或加载。

    为了解决这个问题 precaching 使用。媒体(图片/视频/等)在webview后台同步加载。

    1. 这是全球公认标准的一部分
    2. SDK将侦听 onload

    synchronous XMLHttpRequests ,除非能确定另一种方式,否则广告业务很可能在未来被迫改变标准。

        19
  •  0
  •   bbsimonbb    8 年前

    这是一个很好的理由。然后,我想执行一个http请求,根据结果对输入类型=文件调用click()。这在异步xhr或fetch中是不可能的。回调将丢失上下文“用户操作”,因此将忽略调用click()。同步xhr救了我的命。

    onclick(event){
        //here I can, but I don't want to.
        //document.getElementById("myFileInput").click();
        fetch("Validate.aspx", { method : "POST", body: formData, credentials: "include" })
        .then((response)=>response.json())
        .then(function (validResult) {
            if (validResult.success) {
                //here, I can't.
                document.getElementById("myFileInput").click();
            }
        });
    }
    
        20
  •  0
  •   user2800679 user2800679    6 年前

    同步XHR对于(非生产)内部工具和/或框架开发非常有用。例如,假设您希望在第一次访问时同步加载代码库,如下所示:

    get draw() 
    {
        if (!_draw)
        {
           let file;
           switch(config.option)
           {
               case 'svg':
                  file = 'svgdraw.js';
                  break;
               case 'canvas':
                  file = 'canvasdraw.js';
                  break;
               default:
                  file = 'webgldraw.js';
           }
    
           var request = new XMLHttpRequest();
           request.open('GET', file, false);
           request.send(null);
           _draw = eval(request.responseText);
        }
    
        return _draw;
    }
    

    在你感到紧张和盲目地反刍邪恶的评估之前,请记住,这只是局部测试。对于生产构建,已经设置了\u draw。

    foo.drawLib.draw.something(); //loaded on demand
    

    对于工具和框架(在本地运行)来说,您所能做的限制仅限于您的想象力。不过,在JavaScript世界中,想象力似乎有点有限。