記一次錯:迴圈中呼叫 Promise 的 resolve 的問題
起因
最近在看方方的《造輪子》,我是直接上 vue-cli 3 的,所以在配置上真的下了很大功夫。今天就出現了在迴圈裡呼叫 resolve 函式的問題。
先說說我的配置吧,我是使用 vue-cli 3 + vue-cli-plugin-unit-karma 外掛來配置的 Karma 的。
vue.config.js
vue-cli 3 是不需要 webpack.config.js 的,所以要將配置 Karam 要使用 vue-cli-plugin-unit-karma 外掛,這也是官方推薦的方法,而且這個外掛是和 vue-cli-service 配合的連 package.json 裡的 test 都不用改,直接 vue-cli-service test:unit
一波帶走。這裡面我還使用了全域性 Sass 檔案。
const karmaConfig = require('./karma.config') module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/overwatch-ui/' : '/', css: { loaderOptions: { sass: { // @ -> /src data: ` @import "@/assets/styles/global.scss"; @import "@/assets/styles/reset.scss"; ` } } }, pluginOptions: { karma: { expressServer: undefined, karmaConfig: karmaConfig } } }
karam.config.js
注意,這不是官方所用的那種 karam.config.js 檔案,我的檔案只是返回一個物件,不想在 vue.config.js 裡寫太長了而已。
module.exports = { basePath: '', frameworks: ['mocha', 'chai', 'sinon-chai'], client: { chai: { includeStack: true }, }, files: [ 'tests/**/*.spec.js', 'tests/**/*.spec.ts', ], reporters: ['progress'], port: 9876, colors: true, autoWatch: true, browsers: ['ChromeHeadless'], concurrency: Infinity, }
Input.spec.js
先定義一下 testProperty
函式,因為我理想的情況是多種值都要測,所以應該迴圈一個數組,對數組裡每個值都做一次測試(就是這個程式碼坑了,用回撥好好的,自己非得裝B用 Promise 來玩)。
function testProperty(values) { return new Promise((resolve, reject) => { values.forEach((value) => resolve(value)) }) }
這裡我就不寫那麼多程式碼啦,大家看到這就意會一下就行了。
describe('events', ()=> { it('can be handled', () => { const events = ['click', 'change', 'input'] const eventHandler = sinon.stub() testProperty(events) .then((eventName) => { console.log(eventName) const InputVue = shallowMount(Input, { listeners: { [`${eventName}`]: eventHandler } }) InputVue.find('input').trigger(eventName) expect(eventHandler.called).to.equal(true) }) }) })
說完上面的基本配置,就說說今天的採坑記吧。
沒有 Log?
當我執行 yarn run test
的時候,發現 Karma 跑通了,Yes,第一波 Vue + Karam 配置成功!

哎不對,我的 console.log(eventName)
怎麼沒了?第一反應是 Karam 沒有配置好,因為測試是歸 Karma 管的。然後 Google 了一下,找到了 Karam runner 裡 Karam-Mocha 裡的 Issue

然後馬上去將 browserConsoleLogOptions
和 captureConsole
裡的東西加在 karam.config.js
裡。再次執行,Boom,還是不行,什麼都沒有列印。
config 是誰,它在哪?
雖然這個人很多贊,但是畢竟是個人說辭,不太可靠,他既然說到了 Karma 的配置,那麼官方文件肯定會有說呀。Go,下一步 Karam 官方文件。果然我找到一個配置:

說是在 karam.config.js 裡設定 config.LOG_INFO
的,像這樣:
module.exports = function(config) { config.set({ logLevel: config.LOG_INFO, }); };
但是,這種是不是 vue cli 3 的寫法,這是 vue cli + webpack.config.js 的寫法呀。我了個去,這麼寫 config 都不知道從哪來,常量 LOG_INFO
肯定是 undefined
。
哎,雖然我不能配置 logLevel: config.LOG_INFO
,但是我可以用命令列呀,所以馬上去 package.json 裡改指令碼命令:
"test": "vue-cli-service test:unit --log-level debug"
開 iTerm2,秒寫命令:
yarn run test
但是還是沒 log,當時心態已經到崩潰的邊緣了。
484你這個 0.5 版本的外掛不行啊
現在頭號嫌疑犯就是這個外掛了,因為 --log-level debug
寫在 vue-cli-service
後面,應該是要 vue-cli-service
結合使用的,而這個外掛號稱自己和 vue-cli-service 完美結合,一看版本才 0.5 想:是不是你這個弱智沒寫好外掛就到處裝B了。又去看了這個外掛的 Issue。

嗯。。。還很乾淨。沒法了,現在只有兩條路
- 寫 Issue 給這個外掛作者,可能要等一會才能解決,而且不一定能解決。
- 使用降級 vue-cli,使用 vue-cli 和 webpack.config.js 結合的老方法,一定能行,但是麻煩。
兩個方法都很難受,所以我選擇了第三種方法:在別的地方打 log,是不是真的別的地方都不能打 log 了。所以我在 testProperty
里加了句:
function testProperty(values) { return new Promise((resolve, reject) => { console.log(values) values.forEach((value) => resolve(value)) }) }
結果竟然有 log 了!看來上面的方法全部作廢了,別的地方能 log 說明配置完全正確。
迴圈 resolve 害死人啊
其實我一開始寫的 testProperty
是這樣的:
function testProperty(values, callback) { values.forEach(callback) }
我想了想,是不是有點 low 啊,這樣封裝好像都沒什麼意義,所以使用了 Promise 寫法。而這種寫法只能在迴圈裡呼叫 resolve(value)
才能將陣列元素傳入回撥裡。後面自己再試了試, then()
完了之後只會呼叫一次 resolve(value)
,而不會在迴圈裡多次呼叫,我的這種寫法純粹屬性猜測去寫,所以導致看了半天的 Issue 和 StackOverflow 才慢慢解決了這個問題。
總結
其實我的迴圈呼叫 resolve 是可以打出一個 log 的,因為會呼叫一次,但是前期確實沒有將 karam.config.js 裡的 client
和 browserConsoleLogOptions
配置好,所以沒有 log。
即使加了這個配置選項,可能當時程式碼本來也點問題,所以一直沒發現這種”猜測寫法“帶來的問題。最後解決的時候確實能回想當初寫這樣程式碼的想法:“能不能迴圈 resolve 呢?嗯試試吧”。
總之,這是一次不錯的調錯經歷,調完真的是身心俱疲。