组织ES6模块的时候,一般情况下我们export一些基本类型的变量,复合类型的对象或者函数等等。但有时模块内的代码也会出现异步操作,我们希望模块可以把异步操作的结果export出来供另一个模块调用。这种情况下应该怎么写呢?本文列举两种方法供参考。
利用Promise对象
export导出的变量类型可以是任意的Javascript对象,那么我们可以利用Promise对象帮助我们代理异步操作,将Promise对象导出,在导入Promise的地方添加成功/失败的回调即可。
a.js
import {AsyncStorage} from 'react-native';
export async function getVariable() {
let variable = await AsyncStorage.getItem("variable");
return variable;
}
b.js
import {getVariable} from 'a.js';
getVariable().then(v=>{
//在这里获得模块a真正想导出的值
}).catch(e=>{})
a文件中使用了ES7的async函数,async函数一旦执行会立即返回一个Promise对象,等待所有await后的异步操作结束后改变Promise的状态。如果不使用async await,a文件也可以这样写:
import {AsyncStorage} from 'react-native';
export function getVariable() {
return new Promise((resolve, reject)=>{
AsyncStorage.getItem("variable").then(v=>{
resolve(v);
}).catch(e=>{
reject(e);
})
})
}
直接导出异步结果
a.js
import {AsyncStorage} from 'react-native';
export var variable;
AsyncStorage.getItem("variable").then(v=>{
variable = v; //在回调函数中修改导出变量variable
}).catch(e=>{});
b.js
import {variable} from 'a.js';
console.log(variable); //undefined
setTimeout(()=>{
console.log(variable); //异步操作所获得的值
}, 1000)
需要注意的是,当采用这种方式时,要注意导出变量会发生变化,在引用模块中要特别注意,否则突变的值会对程序造成莫名的bug。
说明
能够使用上述两种方式导出异步操作的结果,原因就在于,ES6的模块与CommonJS是不同的。
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
阮一峰的ES6入门有如下详细的说明:
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。