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