Я использую Axios для вызова конечной точки api, у которой есть индикатор выполнения. Я хотел бы преобразовать onUploadProgress в генератор.
Есть ли способ преобразовать этот код
setProgress({ state: 'syncing', progress: 0 });
await axios.post(uri.serialize(parsedURI), body2, {
headers: { session_id: sessionId },
onUploadProgress: (progress) => {
setProgress({ state: 'syncing', progress: progress.loaded / progress.total });
},
});
setProgress({ state: 'syncing', progress: 1 });
На что-то вроде этого
yield { state: 'syncing', progress: 0 };
await axios.post(uri.serialize(parsedURI), body2, {
headers: { session_id: sessionId },
onUploadProgress: (progress) => {
yield { state: 'syncing', progress: progress.loaded / progress.total };
},
});
yield { state: 'syncing', progress: 1 };
Проблема в доходности внутри onUploadProgress, я думал, что есть способ использовать его, например, когда вы хотите преобразовать обратный вызов в обещание, которое вы используете
new Promise(resolve => fn(resolve));
Возможно, есть какой-нибудь действенный способ сделать это для генераторов, таких как
new Generator(next => fn(next));
2 ответа
Вы можете обновить локальную переменную в обработчике выполнения и опрашивать ее в цикле, пока запрос не будет завершен. Вот набросок:
let delay = n => new Promise(res => setTimeout(res, n));
async function request(n, onProgress) {
for (let i = 0; i < n; i++) {
onProgress(i);
await delay(200);
}
return 'response'
}
async function* requestWithProgress(result) {
let prog = 0,
prevProg = 0,
res = null;
let req = request(10, n => prog = n)
.then(r => res = r);
while (!res) {
await delay(1);
if (prog > prevProg)
yield prevProg = prog;
}
result.res = res;
}
async function main() {
let result = {}
for await (let prog of requestWithProgress(result))
console.log(prog)
console.log(result)
}
main()
Вот еще один вариант, без опроса и лучшего API возврата:
function progressiveAsync(factory) {
let
resultPromise = null,
resultResolver = null,
genPromise = null,
genResolver = null,
stop = {};
resultPromise = new Promise(r => resultResolver = r);
genPromise = new Promise(r => genResolver = r);
async function* gen() {
while (true) {
let r = await genPromise;
if (r === stop) {
break;
}
yield r;
}
}
factory(
val => {
genResolver(val);
genPromise = new Promise(r => genResolver = r);
},
val => {
genResolver(stop);
resultResolver(val);
}
);
return [gen(), resultPromise];
}
//
async function testRequest(opts /* count, onProgress */) {
return new Promise(async res => {
for (let i = 0; i < opts.count; i++) {
opts.onProgress(i);
await new Promise(resolve => setTimeout(resolve, 300));
}
res('response!')
})
}
async function main() {
let [progress, result] = progressiveAsync((next, done) =>
testRequest({
count: 10,
onProgress: next
}).then(done));
for await (let n of progress)
console.log('progress', n)
console.log(await result)
}
main()
factory()
(и в основном остальной код) внутри функции генератора б) поместил genPromise = …
внутри цикл, чтобы избежать дублирования c) полностью отбросить resultPromise
и вместо этого return
результат от генератора d) передать результат в область действия генератора, сохранив его как свойство в stop
вместо звонит resultResolver
.
Я немного изменил код @georg, чтобы можно было использовать этот подход с любой функцией и иметь интерфейс, который ближе к тому, что было запрошено.
async function* createGenerator<T>(factory: (
next: (value: T) => void,
complete: () => void) => void,
): AsyncIterableIterator<T> {
let completed = false;
let value: { value: T } | null = null;
factory(
(v: T) => {
value = { value: v };
},
() => {
completed = true;
},
);
while (!completed) {
if (value != null) {
yield (value as { value: T }).value;
value = null;
}
value = null;
await new Promise(resolve => setImmediate(resolve));
}
}
Так что теперь его можно использовать вот так
yield { state: 'syncing', progress: 0 };
yield* createGenerator<Progress>((next, complete) => {
axios.post(uri.serialize(parsedURI), body, {
headers: { session_id: sessionId },
onUploadProgress: (progress) => {
next({ state: 'syncing', progress: progress.loaded / progress.total });
},
}).then(() => complete());
});
yield { state: 'syncing', progress: 1 };
setImmediate
совершенно неприемлем.
Похожие вопросы
Связанные вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript/JS) и его различных диалектах/реализациях (кроме ActionScript). Обратите внимание, что JavaScript — это НЕ Java. Включите все теги, относящиеся к вашему вопросу: например, [node.js], [jQuery], [JSON], [ReactJS], [angular], [ember.js], [vue.js], [typescript], [стройный] и т. д.