async function_Electron IPC 通信如何使用 async/await 调用?
前言碎語
此想法是在使用 electron 進(jìn)程間通信(IPC)過程中,無法忍受其 API 的使用不友好性而產(chǎn)生。
為了提高代碼可讀性、可維護(hù)性,而不得已造輪子了。
生命在于折騰,其樂無窮。Electron 中 IPC 的通信方式
在 Electron 中分為兩個進(jìn)程:
因?yàn)?Electron 出于安全考慮,渲染進(jìn)程的 API 是有限制的。因?yàn)?web 端可能會加載第三方 js 代碼,不可能讓第三方為所欲為的。
假如在一些場景中我可能要獲取某個文件夾下的文件列表(舉例而已),在 Node 中使用 fs 模塊即可完成,但是在 web 端無法使用 fs API。
這時候就需要使用 IPC 進(jìn)程通信來解決。
就像是 nodejs 中的子進(jìn)程一樣通過 spawn 方法開啟一個子進(jìn)程,兩個進(jìn)程之間只能通過 IPC 協(xié)議通信
主進(jìn)程:
import { IpcMain } from 'electron' import fs from 'fs'// 監(jiān)聽渲染進(jìn)程發(fā)來的 getDir 請求 IpcMain.on('getDir', (event, data) => {fs.readdir('path', (err, files) => {if (err) {// 響應(yīng)渲染進(jìn)程獲取失敗了event.reply('dir-result', 'err')} else {const result = files.map(/* do something */)// 響應(yīng)渲染進(jìn)程獲取到的結(jié)果event.reply('dir-result', result)}}) })渲染進(jìn)程:
const { ipcRenderer } = require('electron')// 先監(jiān)聽主進(jìn)程發(fā)來的消息 ipcRenderer.on('dir-result', (event, data) => {// do something })// 發(fā)送給主進(jìn)程 ipcRenderer.send('getDir', '/Users')代碼很簡單,主進(jìn)程先監(jiān)聽渲染進(jìn)程消息,渲染進(jìn)程再監(jiān)聽主進(jìn)程消息,然后渲染進(jìn)程發(fā)起消息。
很好理解,只不過渲染進(jìn)程的操作過于繁瑣。
假如此邏輯放入vue 組件中,那么需要在 created 中進(jìn)行監(jiān)聽主進(jìn)程消息,在 destroyed 中進(jìn)行解綁改事件。
那么怎么去優(yōu)化此處的體驗(yàn)?zāi)?#xff1f;造輪子唄~
對 IPC 通信方式進(jìn)行二次封裝
先看下面這段代碼:
此處為渲染進(jìn)程部分代碼,以 React 組件舉例。// 渲染進(jìn)程 import React, { useState, useEffect } from 'react' import request from './request'export default function IpcTest () {const [list, setList] = useState([])useEffect(() => {init()}, [])// 進(jìn)行初始化操作const init = async () => {// request 取代 ipcRenderer.send 和 ipcRenderer.onconst result = await request('test', { a: 1 })// result => ['1', '2', '3'] from Main ProcesssetList(result)} }// 主進(jìn)程 import server from './server' // 類似 koa-router 的使用方式 server.use('test', async (ctx, data) => {// data => { a: 1 } from Renderer Processconst result = await doSomething(data)ctx.reply(['1', '2', '3']) })上面代碼以 http 請求的寫法來處理了 IPC 通信。其中 request 與 server 則是進(jìn)行二次封裝后的輪子。
這樣使用 async/await 的方式來處理,是不是就輕松多了?代碼的可讀性、可維護(hù)性也增強(qiáng)了。
而且在封裝的過程中完全可以按照 axios 的 API 來處理,便于代碼遷移(當(dāng)然這里業(yè)務(wù)邏輯的通用性另說)。
廢話不多說下面來看一下處理原理。
Electron IPC 異步封裝request 大致實(shí)現(xiàn)邏輯:
const { ipcRenderer } = require('electron')const _map = new Map()ipcRenderer.on('from-server', (event, params) => {const cb = _map.get(params.symbol)if (typeof cb === 'function') {_map.delete(params.symbol)cb()} })export default request (type, data) {const _symbol = Date.now() + typereturn new Promise(resolve => {_map.set(_symbol, data => {resolve(data)})ipcRenderer.send('from-type', {_symbol, type, data})}) }server 大致實(shí)現(xiàn)邏輯:
import { ipcMain } from 'electron'const _map = new Map()ipcMain.on('from-client', (event, params) => {const reply = function (data) {event.reply('from-server', {_symbol: params._symbol,// data 傳遞給客戶端,最終 resolve 它data})}const ctx = {reply,type: params.type}const cb = _map.get(params.type)if (typeof cb === 'function') {cb(ctx, params.data)} else {// 沒有注冊~} })export default function use (type, callback) {_map.set(type, cb) }一個簡單基礎(chǔ)版的 IPC 封裝就完成了,在此功能上還可以增加一些 timeout、多次調(diào)用只獲取最后一次返回的數(shù)據(jù)類似的功能,這些在原生的 electron IPC API 上是無法實(shí)現(xiàn)的(也可能是我文檔看的少沒有發(fā)現(xiàn))。
測試
起初在做單元測試時,走了彎路。
因?yàn)?IPC 是兩個進(jìn)程之間交互的一個過程,當(dāng)時一直在想如何簡單的啟動一個 electron 容器進(jìn)行測試。
后來又根據(jù)官網(wǎng)介紹想通過 Node 啟動兩個進(jìn)程來模擬 IPC 交互過程,這其實(shí)也是個彎路。
其實(shí)需要做的只要保證 use、request 方法能跑通、保證內(nèi)部邏輯運(yùn)行正確即可。
最后直接模擬了 ipcRenderer (on 和 send)、ipcMain (on 和 reply),將這 4 個方法的輸入與輸出與 electron 提供的 API 表現(xiàn)一致即可達(dá)到目的。
類似于 測試驅(qū)動 模擬一個測試環(huán)境可以讓代碼正常運(yùn)行,且表現(xiàn)一致。
總結(jié)
以上是生活随笔為你收集整理的async function_Electron IPC 通信如何使用 async/await 调用?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中值滤波去除椒盐噪声_pyt
- 下一篇: python读取图片属性_[Python