Open mrdulin opened 6 years ago
util.ts:
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模块中导入并调用这两个方法:
index.ts
import { setToken, getToken } from './util'; setToken(); getToken(); async function main() { getToken(); } main();
如何在util.ts模块中,只调用一次getConfigAsync方法获取配置,此后在setToken和getToken方法被其他模块(index.ts)调用时使用该配置变量
getConfigAsync
setToken
getToken
在模块作用域定义setToken和getToken并导出。(意思是不要使用闭包,class,模块模式等方式导出,在其他模块直接调用setToken和getToken)
在演示解决方案之前,先看个一般的做法:
util-old.ts:
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方法获取远程配置。
getConfig
async/await
IIFE
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)。
import
config
Promise.resolve(conf)
const conf = await config;
等价于
const conf = await Promise.resolve({meta: 'hello'});
conf就是{meta: 'hello'};
conf
{meta: 'hello'}
再次在index.ts和a.ts中调用setToken和getToken方法:
a.ts
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' }
可见,尽管调用了多次调用了setToken和getToken,获取远程配置的getConfigAsync方法,只执行了一次。
并且直接导出了在模块作用域定义的setToken和getToken方法,为什么特意提到这个?可以想想如果不用async/await,而是promise,怎么实现这个需求,并且还能直接导出定义在模块作用域的setToken和getToken这两个方法?
promise
这个例子充分体现了async/await使用同步的代码写法的优势,之前写过各种场景下的6,7个例子和promise作对比,async/await的优势并不是特别明显,例如如果要并发多个异步操作,使用async/await写法是const results = await Promise.all([asyncFn1(), asyncFn2()]),相比直接使用promise,async/await并没有什么显著的优势。
const results = await Promise.all([asyncFn1(), asyncFn2()])
此外,如果在async函数中有多个await,异常处理很容易出现满屏try/catch的情况,这是另外的话题了。
async
await
try/catch
source code: https://github.com/mrdulin/async-await/tree/master/src/demo-7
看起来貌似只是多了一个 async 版本的 IIFE 啊...
@pbdm 更新了最后一段说明
上下文: 环境变量和配置文件在远程服务器上,在当前应用程序中要获取远程服务器上的配置文件,代码示范如下:
util.ts
:在
index.ts
模块中导入并调用这两个方法:需求:
如何在
util.ts
模块中,只调用一次getConfigAsync
方法获取配置,此后在setToken
和getToken
方法被其他模块(index.ts
)调用时使用该配置变量在模块作用域定义
setToken
和getToken
并导出。(意思是不要使用闭包,class,模块模式等方式导出,在其他模块直接调用setToken
和getToken
)在演示解决方案之前,先看个一般的做法:
util-old.ts
:在
index.ts
中调用,输出:可见每一次调用
setToken
或者getToken
,都会执行一次getConfig
方法获取远程配置。解决方案:使用
async/await
+IIFE
util.ts
:这一句是重点,这样可以在
util.ts
模块被import
时,立即执行getConfigAsync
获取远程配置。如果getConfigAsync
方法没有抛出异常,这时候的config
变量等价于Promise.resolve(conf)
。等价于
conf
就是{meta: 'hello'}
;再次在
index.ts
和a.ts
中调用setToken
和getToken
方法:index.ts
:a.ts
:输出:
可见,尽管调用了多次调用了
setToken
和getToken
,获取远程配置的getConfigAsync
方法,只执行了一次。并且直接导出了在模块作用域定义的
setToken
和getToken
方法,为什么特意提到这个?可以想想如果不用async/await
,而是promise
,怎么实现这个需求,并且还能直接导出定义在模块作用域的setToken
和getToken
这两个方法?这个例子充分体现了
async/await
使用同步的代码写法的优势,之前写过各种场景下的6,7个例子和promise
作对比,async/await
的优势并不是特别明显,例如如果要并发多个异步操作,使用async/await
写法是const results = await Promise.all([asyncFn1(), asyncFn2()])
,相比直接使用promise
,async/await
并没有什么显著的优势。此外,如果在
async
函数中有多个await
,异常处理很容易出现满屏try/catch
的情况,这是另外的话题了。source code: https://github.com/mrdulin/async-await/tree/master/src/demo-7