同步调用和异步调用同时存在导致的混乱

其实在Promise之外也存在这个问题,这里我们以一般的使用情况来考虑此问题。
这个问题的本质是接收回调函数的函数,会根据具体的执行情况,可以选择是以同步还是异步的方式对回调函数进行调用。
下面我们以 onReady(fn) 为例进行说明,这个函数会接收一个回调函数进行处理。

mixed-onready.js

function onReady(fn) {
  var readyState = document.readyState;
  if (readyState == ‘interactive‘ || readyState === ‘complete‘) {
    fn();
  } else {
    window.addEventListener(‘DOMContentLoaded‘, fn);
  }
}
onReady(function () {
  console.log(‘DOM fully loaded and parsed‘);
});
console.log(‘==Starting==‘);

mixed-onready.js会根据执行时的DOM是否装载完毕来决定是对回调函数进行同步或者是异步的调用。

如果在调用onReady之前DOM已经载入的话
  对回调函数进行同步调用
如果在调用onReady之前DOM还没有载入的话
  通过注册 DOMContentLoaded 事件监听器来对回调函数进行异步调用
因此,如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。
为了解决这个问题,我们可以选择统一使用异步调用方法。

async-onready.js

function onReady(fn) {
    var readyState = document.readyState;
    if (readyState == ‘interactive‘ || readyState === ‘complete‘) {
        setTimeout(fn, 0);
    } else {
        window.addEventListener(‘DOMContentLoaded‘, fn);
    }
}
onReady(function () {
    console.log(‘DOM fully loaded and parsed‘);
});
console.log(‘==Starting==‘);

关于这个问题,在 Effective JavaScript 23 的 第67项 不要对异步回调函数进行同步调用中也有详细介绍。
  • 绝对不能对异步回调函数(即使在数据已经就绪)进行同步调用。
  • 如果对异步回调函数进行同步调用的话,处理顺序可能会与预期不符,可能带来意料之外的后果。
  • 对异步回调函数进行同步调用,还可能导致栈溢出或异常处理错乱等问题。
  • 如果想在将来某时刻调用异步回调函数的话,可以使用 setTimeout等异步API。
                          — David Herman Effective JavaScript

时间: 2024-10-09 21:16:49

同步调用和异步调用同时存在导致的混乱的相关文章

同步调用与异步调用

提交任务的两种方式:同步调用与异步调用 同步调用 # 同步调用:提交任务后,就在原地等待任务执行完毕,拿到结果,再执行下一行代码.导致程序是串行执行 import time import random from concurrent.futures import ThreadPoolExecutor def work(name): print("%s is working" % name) time.sleep(random.randint(7, 13)) res = random.r

C#(同步调用、异步调用、异步回调)

本文将主要通过“同步调用”.“异步调用”.“异步回调”三个示例来讲解在用委托执行同一个“加法类”的时候的的区别和利弊. 首先,通过代码定义一个委托和下面三个示例将要调用的方法: public delegate int AddHandler(int a,int b);    public class 加法类    {        public static int Add(int a, int b)        {            Console.WriteLine("开始计算:&quo

C#:同步调用、异步调用、异步回调

ylbtech-C#:同步调用.异步调用.异步回调 1.返回顶部 1. 本文将主要通过“同步调用”.“异步调用”.“异步回调”三个示例来讲解在用委托执行同一个“加法类”的时候的的区别和利弊. 首先,通过代码定义一个委托和下面三个示例将要调用的方法: public delegate int AddHandler(int a,int b);    public class 加法类    {        public static int Add(int a, int b)        {    

Code-C#-Delegate:委托(delegate)的三种调用方式:同步调用,异步调用,异步回调

ylbtech-Code-C#-Delegate:委托(delegate)的三种调用方式:同步调用,异步调用,异步回调 1.返回顶部 1. 下面为即将被调用的方法: public delegate int AddHandler(int a, int b); public class 加法类 { public static int Add(int a, int b) { Console.WriteLine("开始计算:" + a + "+" + b); Thread.

服务消费端泛化调用与异步调用

本文借用dubbo.learn的Dubbo API方式来解释原理. 服务消费端泛化调用 前面我们讲解到,基于Spring和基于Dubbo API方式搭建简单的分布式系统时,服务消费端引入了一个SDK二方包,里面存放着服务提供端提供的所有接口类,之所以需要引入接口类是因为服务消费端一般是基于接口使用JDK代理实现远程调用的. 泛化接口调用方式主要在服务消费端没有API接口类及模型类元(比如入参和出参的POJO类)的情况下使用.其参数及返回值中没有对应的POJO类,所以所有POJO均转换为Map表示

java三种调用方式(同步调用/回调/异步调用)

1:同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用 2:回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口: 3:异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口 ) 实例2:老师平时学生布置任务后不可能一直等待和监督学生完成,老师通常会告诉学生,任务完成后给他打个电话或者发个信息,那么学生给老师返回结果的过程需要老师信息,这就是一个回调的过程.

C#委托调用(同步调用,异步调用,异步回调)

#region 委托回调 static void delegateTest() { Console.WriteLine("同步调用"); SubDelegate subDel = Sub; var result = subDel.Invoke(3, 4); Console.WriteLine("继续"); Console.WriteLine(result); Console.ReadKey(); Console.WriteLine("异步调用")

WebService 同步调用,异步调用

阅读目录 一:添加WebService服务 二:添加“客户程序”(这里用winform)用于调用WebService服务 三:异步调用过程解释 一:添加WebService服务 1.添加一个空网站项目,2.在项目里面添加一个WebService服务(WebServiceTest.asmx),3.添加“HelloWorld”方法 代码:(WebServiceTest.asmx) using System; using System.Collections.Generic; using System

C# 委托的同步调用和异步调用

委托的Invoke方法用来进行同步调用.同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行. 同步调用的例子: using System; using System.Threading; public delegate int AddHandler(int a, int b); public class Foo { static void Main() { Console.WriteLine("**********SyncInvokeTest***********