背景
我司做了一個(gè)可視化大屏的項(xiàng)目,但基于配置的組件不可能窮盡所有情況,因此計(jì)劃開發(fā)js交互功能,讓用戶可以通過js進(jìn)行二次開發(fā)言缤。
遇到的問題
直接用new Function()
去執(zhí)行用戶的代碼,一開始寫靜態(tài)數(shù)據(jù)demo的時(shí)候沒發(fā)現(xiàn)有啥問題禁灼。但當(dāng)我去請(qǐng)求動(dòng)態(tài)數(shù)據(jù)的時(shí)候管挟,發(fā)現(xiàn)我很想用await
,然而直接使用會(huì)報(bào)SyntaxError: await is only valid in async function at new Function (<anonymous>)
的錯(cuò)弄捕。
曲折的解決問題
step1
google查詢如何在new Function里使用async
僻孝,查到了這篇文章,大喜守谓。主要代碼:
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
const fetchPage = new AsyncFunction("url", "return await fetch(url);");
fetchPage("/").then(response => { ... });
因?yàn)樵恢С种苯荧@取AsyncFunction
穿铆,所以這里通過獲取async
函數(shù)的構(gòu)造函數(shù)原型來獲取。
屁顛屁顛地跑去項(xiàng)目里用了一下斋荞,發(fā)現(xiàn)并沒有生效荞雏。
step2
開始脫離項(xiàng)目寫demo,把問題最小化平酿。代碼如下:
function test() {
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
const code =
'const name = await Promise.resolve(stage.name); \n console.log(name)';
const func = new AsyncFunction('stage', code);
func({ name: '1' }).then((response) => {
console.log(response);
});
}
test();
在node.js和chrome環(huán)境下均可以正確執(zhí)行
step3
思考項(xiàng)目中用到了什么凤优,在想不會(huì)是webpack
的鍋吧,但不能確定蜈彼。
于是回到項(xiàng)目筑辨,在瀏覽器報(bào)錯(cuò)后跳轉(zhuǎn)到報(bào)錯(cuò)的地方,是一個(gè)VMxxxx
文件幸逆,截屏如下:
看到我自己的code前面被包了一層(function anonymous(){mycode})
棍辕,猜想會(huì)不會(huì)跟這個(gè)有關(guān),但查了一下VMxxxx
文件是js虛擬機(jī)編譯生成的文件还绘,照理說瀏覽器對(duì)同樣的代碼執(zhí)行流程都一樣楚昭,肯定都會(huì)經(jīng)歷編譯階段,既然在瀏覽器直接執(zhí)行不報(bào)錯(cuò)蚕甥,應(yīng)該跟這個(gè)無關(guān)哪替。
接著打斷點(diǎn)看看發(fā)生了什么。在報(bào)錯(cuò)的語句前打了斷點(diǎn)菇怀,然后在瀏覽器定義AsyncFunction
并執(zhí)行,并沒有報(bào)錯(cuò)晌块。
正當(dāng)我納悶的時(shí)候爱沟,想著應(yīng)該對(duì)比一下項(xiàng)目里的AsyncFunction
和我直接在瀏覽器定義的AsyncFunction
有什么區(qū)別,就分別打印了一下匆背,終于找到了原因呼伸,項(xiàng)目里的AsyncFunction
不知道啥時(shí)候悄悄變成了Function
,怪不得又不能用await
了。
step4
所以思考AsyncFunction
為什么會(huì)變成Function
括享。似乎只有webpack
可能產(chǎn)生影響搂根,于是在另一個(gè)用webpack
打包的項(xiàng)目中嘗試了一下,也報(bào)一樣的錯(cuò)铃辖,基本上鎖定是webpack
的問題了剩愧。想到我們一般在項(xiàng)目里會(huì)用babel
把js
編譯成es5
,而es5
不認(rèn)識(shí)async
娇斩,自然也不認(rèn)識(shí)它的原型仁卷,所以我把AsyncFunction
的定義放到一個(gè)單獨(dú)的文件,不讓webpack
編譯犬第,終于可以順利執(zhí)行了锦积!
結(jié)論
- 如果在原生支持es7的項(xiàng)目(不經(jīng)過babel等轉(zhuǎn)義)中使用,直接通過
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
獲取到原型歉嗓,就可以像new Function
一樣使用了 - 目前前端工程化項(xiàng)目為了兼容性丰介,一般都會(huì)用到babel轉(zhuǎn)義,這時(shí)候就需要先把
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
這個(gè)定義放到單獨(dú)的文件鉴分,并且不轉(zhuǎn)義該文件哮幢,才能在執(zhí)行的時(shí)候獲取正確的類型。