mrdulin / blog

Personal Blog - 博客 | 编程技术,软件,生活

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

async/await获取远程环境变量的一个例子

mrdulin opened this issue · comments

上下文: 环境变量和配置文件在远程服务器上,在当前应用程序中要获取远程服务器上的配置文件,代码示范如下:

util.ts:

async function getConfigAsync() {
  console.log('getConfigAsync');
  return {
    meta: await 'hello'
  };
}

async function setToken() {
  //TODO: 获取config
  console.log('setToken: ', conf);
}

async function getToken() {
  //TODO: 获取config
  console.log('getToken: ', conf);
}

export {setToken, getToken}

index.ts模块中导入并调用这两个方法:

import { setToken, getToken } from './util';

setToken();
getToken();

async function main() {
  getToken();
}

main();

需求:

  1. 如何在util.ts模块中,只调用一次getConfigAsync方法获取配置,此后在setTokengetToken方法被其他模块(index.ts)调用时使用该配置变量

  2. 在模块作用域定义setTokengetToken并导出。(意思是不要使用闭包,class,模块模式等方式导出,在其他模块直接调用setTokengetToken

在演示解决方案之前,先看个一般的做法:

util-old.ts:

function getConfig() {
  console.log('getConfig');
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ meta: 'hello' });
    }, 3000);
  });
}

async function setToken() {
  const conf = await getConfig();
  console.log('setToken: ', conf);
}

async function getToken() {
  const conf = await getConfig();
  console.log('getToken: ', conf);
}

export { setToken, getToken };

index.ts中调用,输出:

getConfig
getConfig
getConfig
setToken:  { meta: 'hello' }
getToken:  { meta: 'hello' }
getToken:  { meta: 'hello' }

可见每一次调用setToken或者getToken,都会执行一次getConfig方法获取远程配置。

解决方案:使用async/await + IIFE

util.ts:

async function getConfigAsync() {
  console.log('getConfigAsync');
  return {
    meta: await 'hello'
  };
}

const config: Promise<any> = (async () => await getConfigAsync())();

async function setToken() {
  const conf = await config;
  console.log('setToken: ', conf);
}

async function getToken() {
  const conf = await config;
  console.log('getToken: ', conf);
}

export { setToken, getToken };
const config: Promise<any> = (async () => await getConfigAsync())();

这一句是重点,这样可以在util.ts模块被import时,立即执行getConfigAsync获取远程配置。如果getConfigAsync方法没有抛出异常,这时候的config变量等价于Promise.resolve(conf)

const conf = await config;

等价于

const conf = await Promise.resolve({meta: 'hello'});

conf就是{meta: 'hello'};

再次在index.tsa.ts中调用setTokengetToken方法:

index.ts:

import { setToken, getToken } from './util';

import './a';

setToken();
getToken();

async function main() {
  getToken();
}

main();

a.ts:

import { setToken, getToken } from './util';

setToken();
getToken();

输出:

getConfigAsync
setToken:  { meta: 'hello' }
getToken:  { meta: 'hello' }
setToken:  { meta: 'hello' }
getToken:  { meta: 'hello' }
getToken:  { meta: 'hello' }

可见,尽管调用了多次调用了setTokengetToken,获取远程配置的getConfigAsync方法,只执行了一次。

并且直接导出了在模块作用域定义的setTokengetToken方法,为什么特意提到这个?可以想想如果不用async/await,而是promise,怎么实现这个需求,并且还能直接导出定义在模块作用域的setTokengetToken这两个方法?

这个例子充分体现了async/await使用同步的代码写法的优势,之前写过各种场景下的6,7个例子和promise作对比,async/await的优势并不是特别明显,例如如果要并发多个异步操作,使用async/await写法是const results = await Promise.all([asyncFn1(), asyncFn2()]),相比直接使用promiseasync/await并没有什么显著的优势。

此外,如果在async函数中有多个await,异常处理很容易出现满屏try/catch的情况,这是另外的话题了。

source code: https://github.com/mrdulin/async-await/tree/master/src/demo-7


Flag Counter

看起来貌似只是多了一个 async 版本的 IIFE 啊...

@pbdm 更新了最后一段说明