是否可以将saga迭代器转换为常规承诺?

时间:2017-07-08 19:23:23

标签: generator es6-promise redux-saga

我正在构建keepassxc webextension的抽象层。它使用redux-saga通道使外观chrome messaging同步。它工作(非)令人惊讶的好。但是我想要完全抽象redux-saga,它看起来像正常函数返回Promise。

TL;博士

KeePassXC-browser将是浏览器扩展程序,允许从浏览器中检索存储在KeePassXC应用程序中的密码。 有两种可能的通信协议:HTTP和NativeClient。所以我决定使用typescript接口,根据通信协议,将有两个实现此接口的类。

接口:

interface Keepass {
  getDatabaseHash(): Promise<string>;
  getCredentials(origin: string, formUrl: string): Promise<KeepassCredentials[]>;
  associate(): Promise<KeepassAssociation>;
  isAssociated(dbHash: string): Promise<boolean>;
}
表示HTTP通信协议的

First implementation正在使用fetch api,它已经基于Promise,因此实现是直接的,100%符合此接口。

代表NativeClient协议的

Second implementation使用redux-saga(效果和通道)使异步消息传递看起来像同步函数调用。它有点复杂,但工作得很好并且涵盖边缘情况,这很难以任何其他方式处理,因为native messaging是基于标准输入和标准输出流的协议,因此请求和响应可以交错,订购等......

实际问题我没有解决,是第二个实现没有实现接口,因为它的生成器不是Promises。

基本上想用返回Promise的函数转换(换行)saga迭代器函数。有一个很好的co library基本上可以为普通的生成器做到这一点。但似乎不适用于redux传奇。

function* someGenerator() {
  const state = yield select(); // execution freeze here when called from wrapper
  const result = yield call(someEffect);
  return result;
}

function wrapper() {
  return co(someGenerator); // returns Promise
}

这可能吗?如果是这样,我做错了什么?

1 个答案:

答案 0 :(得分:0)

Redux-saga基于生成器函数的特殊原因 - 允许将分离的异步操作分离为已分离的部分,并从位于内部saga进程管理器的一个端点管理它们。相反,在一般情况下,Promise是一个自我的东西,不能被部分执行。换句话说,Promises管理它们所在的控制流,而生成器由外部控制流管理。

  

yield select(); //从包装器调用时执行冻结

您的主要误解是假设select实际执行了一些异步操作。不,它只是在该点上暂停函数somegenatator并将控制转移到redux-saga引擎,后者知道与返回值有关,并且可能表示异步进程(可能没有 - 这没关系) 当进程完成时,saga引擎恢复生成器,并将返回值传递给它。

您可以在selecthttps://github.com/redux-saga/redux-saga/blob/master/src/internal/io.js#L139)的源代码中轻松查看。它只返回一个具有某种结构的对象,可以通过saga引擎理解,然后引擎执行实际操作,并以generatorName.next(resultValue)格式调用您的生成器。

UPD。从理论上说,你可以把它包装成可重新分配的承诺,但它不是可用的案例

// Your library code

function deferredPromise() {
  let resolver = null;
  const promise = new Promise(resolve => (resolver = resolve));
  return [
    resolver,
    promise
  ];
}

function generateSomeGenerator() {

let [ selectDoneResolve, selectDonePromise ] = deferredPromise();

const someGenetator =  function* () {
  const state = yield select(); // execution freeze here when called from wrapper
  const [newSelectDoneResolve, newSelectDonePromise] = deferredPromise();
  selectDoneResolve({
    info: state, nextPromise: newSelectDonePromise
  });
  selectDoneResolve = newSelectDoneResolve;
  selectDonePromise = newSelectDonePromise;

  const result = yield call(someEffect);
  return result;
}

return {
  someGenetator,
  selectDonePromise
};

}

const { someGenetator: someGenetatorImpl, selectDonePromise } = generateSomeGenerator();

export const someGenetator = someGenetatorImpl;

// Wrapper for interface

selectDonePromise.then(watchDone)

function watchDone({ info, nextPromise }) {
   // Do something with your info
   nextPromise.then(watchDone);
}
相关问题