Javascript 使用promise而不使用async/await时,Electron open对话框挂起
我在打开对话框窗口时遇到了一个非常奇怪的错误。每当我打开它,它就会挂起,应用程序就会冻结 逻辑很简单,我有一个助手,可以使用Javascript 使用promise而不使用async/await时,Electron open对话框挂起,javascript,reactjs,typescript,redux,electron,Javascript,Reactjs,Typescript,Redux,Electron,我在打开对话框窗口时遇到了一个非常奇怪的错误。每当我打开它,它就会挂起,应用程序就会冻结 逻辑很简单,我有一个助手,可以使用typescriptfsa库创建异步操作。它的目的是调用承诺,并在完成时调用完成/失败的操作。这不是这个助手的问题,因为它适用于应用程序中的其他100个epics,但它可能会与electron dialog发生冲突 export function makeAsyncEpic<T, P, S>( actionCreator: AsyncActionCreato
typescriptfsa
库创建异步操作。它的目的是调用承诺,并在完成时调用完成/失败的操作。这不是这个助手的问题,因为它适用于应用程序中的其他100个epics,但它可能会与electron dialog发生冲突
export function makeAsyncEpic<T, P, S>(
actionCreator: AsyncActionCreators<T, P, S>,
asyncMethod: (params: T, state: ApplicationState, action$) => Promise<P>,
filter?: (action$: Observable<Action>, state: ApplicationState) => boolean,
) {
return makeObservableEpic(actionCreator, (p, s, a) => Observable.fromPromise(asyncMethod(p, s, a)), filter);
}
export function makeObservableEpic<T, P, S>(
{ started, done, failed }: AsyncActionCreators<T, P, S>,
observable: (params: T, state: ApplicationState, action$) => Observable<P>,
filter?: (action$: Observable<Action>, state: ApplicationState) => boolean,
) {
return (action$: Observable<Action>, store: { getState: () => ApplicationState }) =>
action$
.filter(started.match)
.filter(() => (filter === undefined ? true : filter(action$, store.getState())))
.switchMap(action =>
observable(action.payload, store.getState(), action$)
.map(result => {
return done({
params: action.payload,
result,
});
})
.catch(error => {
return Observable.of(
failed({
params: action.payload,
error,
}),
);
}),
);
}
令人惊讶的是如果我把它改成
export const openDirectoryEpic = makeAsyncEpic(actions.openRepository, async () => {
const directory = await mainProcess.openDirectory();
return directory;
});
它很好用。这不是等价物吗?可能的原因是什么
编辑:
我甚至可以在这里删除async/await,并将其像这样放置,它可以工作:
export const openDirectoryEpic1 = makeAsyncEpic(actions.openRepository, () => mainProcess.openDirectory());
难道不是()=>mainProcess.openDirectory()
等同于mainProcess.openDirectory
EDIT2:openDirectory的实现方式如下:
import { dialog, ipcMain } from "electron";
import { mainWindow } from "./main";
export const openDirectory = (): Promise<{ directory: string }> =>
new Promise((resolve, reject) => {
console.log("Opening dialog");
const property: "openDirectory" = "openDirectory";
const options = {
title: "Select Repository",
properties: [property],
};
try {
dialog.showOpenDialog(mainWindow, options, (files: string[]) => {
if (files && files.length === 1) {
resolve({ directory: files[0] });
} else {
reject(`Error when opening directory: ${files}`);
}
});
} catch (err) {
reject(err);
}
});
import{dialog,ipcMain}来自“electron”;
从“/main”导入{mainWindow};
export const openDirectory=():Promise=>
新承诺((解决、拒绝)=>{
console.log(“打开对话框”);
常量属性:“openDirectory”=“openDirectory”;
常量选项={
标题:“选择存储库”,
属性:[属性],
};
试一试{
showOpenDialog(主窗口,选项,(文件:string[])=>{
if(files&&files.length==1){
解析({目录:文件[0]});
}否则{
拒绝(`打开目录时出错:${files}`);
}
});
}捕捉(错误){
拒绝(错误);
}
});
当您使用远程
时,您可能需要注意一件重要的事情:您正在请求同步操作
当调用远程对象的方法、调用远程函数或使用远程构造函数(函数)创建新对象时,实际上是在发送同步进程间消息
如果您希望具有异步行为,而不是使用
remote
在进程之间设置异步ipc通道,并让主进程为异步ipc请求行为。当您使用remote
时,您可能需要注意一件重要的事情:您正在请求同步操作
当调用远程对象的方法、调用远程函数或使用远程构造函数(函数)创建新对象时,实际上是在发送同步进程间消息
如果希望具有异步行为,请不要在进程之间使用
remote
设置异步ipc通道,并让主进程为异步ipc请求行为。哪里是openDirectory()
除非传递回调,否则该showOpenDialog
是同步的
如果要调用showOpenDialog
并让它返回承诺,则必须正确包装它。这是我们从渲染器执行的操作,它不会挂起:
const { remote } = require('electron');
public selectDirectory(mainWindow: BrowserWindow, defaultPath: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
remote.dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory'],
defaultPath: defaultPath
}, names => {
resolve(names ? names[0] : undefined);
});
});
}
const{remote}=require('electron');
公共选择目录(主窗口:BrowserWindow,默认路径:string):承诺{
返回新承诺((解决、拒绝)=>{
remote.dialog.showOpenDialog(主窗口{
属性:['openDirectory'],
defaultPath:defaultPath
},name=>{
解析(名称?名称[0]:未定义);
});
});
}
openDirectory()在哪里
除非传递回调,否则该showOpenDialog
是同步的
如果要调用showOpenDialog
并让它返回承诺,则必须正确包装它。这是我们从渲染器执行的操作,它不会挂起:
const { remote } = require('electron');
public selectDirectory(mainWindow: BrowserWindow, defaultPath: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
remote.dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory'],
defaultPath: defaultPath
}, names => {
resolve(names ? names[0] : undefined);
});
});
}
const{remote}=require('electron');
公共选择目录(主窗口:BrowserWindow,默认路径:string):承诺{
返回新承诺((解决、拒绝)=>{
remote.dialog.showOpenDialog(主窗口{
属性:['openDirectory'],
defaultPath:defaultPath
},name=>{
解析(名称?名称[0]:未定义);
});
});
}
使用makeAsyncEpic(actions.openRepository,mainProcess.openDirectory)
隐式地将所有参数传递给openDirectory函数,并且electron.remote
需要打包/包装每个参数,然后才能将其发送到主处理器。
在您的例子中,最后一个参数是一个可观察的
类型,Electron可能在打包时遇到问题
使用makeAsyncEpic(actions.openRepository,()=>mainProcess.openDirectory())
不向openDirectory
函数传递任何参数,因此Electron没有任何问题
我猜下面的语法(p,s,a)=>mainProcess.openDirectory(p,s,a)
将导致与第一个相同的问题。使用makeAsyncEpic(actions.openRepository,mainProcess.openDirectory)
隐式地将所有参数传递给openDirectory函数,并且electron.remote
需要打包/包装每个参数,然后才能将其发送到主处理器。
在您的例子中,最后一个参数是一个可观察的
类型,Electron可能在打包时遇到问题
使用makeAsyncEpic(actions.openRepository,()=>mainProcess.openDirectory())
不向openDirectory
函数传递任何参数,因此Electron没有任何问题
我猜下面的语法
(p,s,a)=>mainProcess.openDirectory(p,s,a)
将导致与第一个相同的问题。我猜在您的第一个(冻结)解决方案中,您传递的不是承诺,而是某种值。尝试传递mainProcess.openDirectory()
而不是mainProcess.openDirectory
。您在工作示例中这样做了,不知道为什么不在冻结示例中这样做:)mainProcess.openDirectory相当于()=>mainProcess.openDirectory()不,它不是。这怎么可能是等价的呢?您甚至在另一条评论中指出,openDirector