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



我正在为 keepassxc webextension 构建抽象层。它使用 redux-saga 通道来使外观 chrome 消息同步。它的工作(不(出奇地好。但是,我想完全抽象redux-saga,它看起来像返回Promise的正常函数。

tl;博士

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

接口:

interface Keepass {
getDatabaseHash(): Promise<string>;
getCredentials(origin: string, formUrl: string): Promise<KeepassCredentials[]>;
associate(): Promise<KeepassAssociation>;
isAssociated(dbHash: string): Promise<boolean>;
}

表示HTTP通信协议的第一个实现是使用fetch api,它已经基于Promise,因此实现是直接的,并且100%符合此接口。

表示 NativeClient 协议的第二个实现是使用 redux-saga(效果和通道(使异步消息传递看起来像同步函数调用。它有点复杂,但工作得很好,涵盖了边缘情况,这很难以任何其他方式处理,因为本机消息传递是基于标准输入和标准输出流的协议,因此请求和响应可以交错、乱序等......

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


基本上想转换(包装(saga 迭代器函数与返回 Promise 的函数。有一个很好的 co 库,基本上可以为普通发电机做到这一点。但似乎不适用于 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
}

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

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

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

你的主要误解是假设select实际执行一些异步操作。不,它只是在这一点上暂停函数somegenatator并将控制权转移到 redux-saga 引擎,它知道这与返回值有关,也许是状态异步过程(也许没有 - 没关系( 处理完成后,saga 引擎恢复生成器,并将返回值传递给它。

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

上。纯粹从理论上讲,你可以将其包装为可重新分配的承诺,但它不是可用的情况

// 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);
}

最新更新