bug: 引入SDK作为登录模块,SDK尚未加载完毕,此时调用SDK相关的API会报错

112 阅读2分钟

一开始加载用户信息存储在sdk里,但是初次加载时,sdk尚未加载完毕,无法获取用户信息等

//umi 框架里 getInitialState只会在初始化时执行,无论此时sdk是否加载完成,
//如果sdk没有加载完成则无法获取用户信息,所以要确保sdk完成加载,并获取到相应的用户信息才能退出这个函数
export async function getInitialState(): Promise<{
  settings?: Partial<LayoutSettings>;
  currentUser?: Record<string, any>;
  fetchUserInfo?: () => Promise<Record<string, any> | undefined>;
}> {
  let currentUser: Record<string, any> | undefined = await fetchUserInfo();

  return {
    fetchUserInfo,
    currentUser,
    settings: {},
  };
}

可以通过定时器+promise来实现,直到sdk加载完毕后进行相关操作,未加载完毕则通过定时器返反复获取sdk的加载情况,才把用户信息resolve出去。 如果不用promise的resolve,单纯使用定时器无法实现该功能,因为定时器无论结果如何,都会接着执行下面的代码,只是隔了一段时间后接着执行定时器里的内容而已。在定时器第一次执行,sdk尚未加载完成,拿不到用户信息,此时fetchUserInfo()已经返回undefined,则getInitialState()已经拿到用户信息为undefined,无论后续定时器再执行拿到结果,getInitialState()的值已经不再变化

const fetchUserInfo = (): Promise<Record<string, any> | undefined> => {

  const getUserInfo = async () => {
    if (!window?.jsapi) {
      return undefined;
    } else {
      let info = await window?.jsapi?.login.getUserInfo();
      // 没有登录则没有用户信息
      if (!info) return {};
  
      // 登录的用户信息
      const { userId } = info;
      let User;
      try {
        // 获取该用户邮箱是否为渠道邮箱
        User = await getUserEmailAuthentication(userId);
  
      } catch (error) {
        User = { success: false} ;
      }
      if (!User?.success) {
        message.error('User email has no corresponding channel account.');
      }
      const userInfo = {
        ...info,
        ...User?.data,
        UserEmailAuthentication: User?.success,
      };
     
      return userInfo;
    }
  };

  return new Promise((resolve) => {
    let userInfo;
    const intervalId = setInterval(() => {
      userInfo = getUserInfo();
      if (userInfo) {
        clearInterval(intervalId);
        resolve(userInfo);
      }
    }, 1000);
  });
};

总结: 通过script标签引入外部js时,确保js已经加载完毕后,再调用相关的功能