отмена асинхронной цепочки вызовов в javascript - PullRequest
2 голосов
/ 23 апреля 2020

Я смотрю на проблемы отмены асинхронной c цепочки ожидания в javascript, и я наткнулся на этот твит , где автор заявляет, что для отмены такой цепочки:

  call1();
  call2();
  call3();

Вам нужно было бы обернуть их так:

 try {
    await Promise.race([
      call1(),
      isCanceled()
    ]);
  }
  catch (err) {
    if (err instanceof CancelationError) return;
  }

 try {
    await Promise.race([
      call2(),
      isCanceled()
    ]);
  }
  catch (err) {
    if (err instanceof CancelationError) return;
  }

 try {
    await Promise.race([
      call3(),
      isCanceled()
    ]);
  }
  catch (err) {
    if (err instanceof CancelationError) return;
  }

Какой тип делает с тех пор, как только вы go в await, вы никогда не выйдете из этого, если не обещание либо разрешает или отклоняет.

Мне сказали, что вы можете отменить всю цепочку, используя генераторы, без переноса каждого вызова.

Может кто-нибудь объяснить, как вы можете свернуть всю цепочку, не упаковывая каждую асин c звоните

Ответы [ 5 ]

1 голос
/ 23 апреля 2020

Да, вы можете обернуть это в генераторе, который выдает обещания, а затем для каждого обещания выполняется гонка с отменой:

async function run(cancel, fn) {
  const gen = fn();
  let result = gen.next();
  let next;
  while (!result.done) {
    try {
      next = await Promise.race([cancel, result.value]);
    } catch (err) {
      if (err instanceof CancelationError)
        result = gen.return();
      else
        result = gen.throw(err);
      continue;
    }
    result = gen.next(next);
  }
}

run(isCanceled(), function*() {
  yield call1();
  yield call2();
  yield call3();
});
1 голос
/ 23 апреля 2020

Это не лучшее решение, но вы можете опросить переменную отмены внутри обещания:

 const delay = (t) => new Promise(resolve=>setTimeout(resolve,t))//testing purpose

const call1 = () => new Promise(resolve=>
    {setTimeout(()=>{console.log("call1 finished");resolve("call1")},1000)})
const call2 = () => new Promise(resolve=>
    {setTimeout(()=>{console.log("call2 finished");resolve("call2")},1000)})
const call3 = () => new Promise(resolve=>
    {setTimeout(()=>{console.log("call3 finished");resolve("call3")},1000)})

let cancelled = false;

const isCancelled = () => {
    return new Promise((resolve,reject)=>{
        const a = setInterval(()=>
             {
                 if(cancelled){reject("cancelled");clearInterval(a)}
             },50)
    })
}

async function process(){
    try{ 
         await Promise.race([call1(),isCancelled()])
         await Promise.race([call2(),isCancelled()])
         await Promise.race([call3(),isCancelled()])
       }
       catch(e){console.log(e)}
}
   
async function run(){
   console.log("test case 1 :")
   process();
   setTimeout(()=>cancelled=true,500)//test case 1 - only run call1()

   await delay(3000);cancelled = false
   
   console.log("test case 2 :")
   process();
   setTimeout(()=>cancelled=true,1500)//test case 2 - run call1() and call2()

   await delay(3000);cancelled = false

   console.log("test case 3 :")
   process();
   setTimeout(()=>cancelled=true,3000)//test case 3 - run call calls
}
run()

В трех приведенных выше тестовых случаях:

  • call1 () завершает все три
  • call2 () завершает последние два
  • call3 () завершает только последний
0 голосов
/ 23 апреля 2020

Требуется один генератор для создания функции генератора паузы и бегуна.

генератор:

const generator = function* (cancelable, ...promies) {
  for (let i = 0; i < promies.length; i++) {
    yield Promise.race([promies[i](), cancelable]);
  }
};

бегун:

async function runner(caller) {
  let finished = false;

  do {
    let { done, value } = caller.next();
    try {
      const data = await value;
      console.log(data);
      finished = done;
    } catch (error) {
      finished = true;
      console.log(error);
    }
  } while (!finished);
}

const generator = function* (cancelable, ...promies) {
  for (let i = 0; i < promies.length; i++) {
    yield Promise.race([promies[i](), cancelable]);
  }
};

async function runner(caller) {
  let finished = false;

  do {
    let { done, value } = caller.next();
    try {
      const data = await value;
      console.log(data);
      finished = done;
    } catch (error) {
      finished = true;
      console.log(error);
    }
  } while (!finished);
}
runner(generator(cancelable(), call, call, call, call, call));

function cancelable() {
  return new Promise((r, rej) => {
    setTimeout(() => {
      rej("CANCELED");
    }, 2000);
  });
}

let count = 0;
function call() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(count++);
    }, 500);
  });
}
0 голосов
/ 23 апреля 2020

Генератор не требуется, асиновая c функция-обертка может помочь:

function cancellableAsyncChain(...functions) {
  let cancelled = false;
  async function callFunctions() {
    for (let f of functions) {
      if (cancelled) {
        break; // for if you want the promise to resolve
        throw new CancellationError(); // for if you want the promise to reject
      }
      await f();
    }
  }
  const cancel () => {
    cancelled = true;
  }
  const promise = callFunctions();
  promise.cancel = cancel;
  return promise;
}

// Usage
async function f1() {}
async function f2() {}
async function f3() {}

let cancellablePromise = cancellableAsyncChain(f1, f2, f3);
setTimeout(() => cancellablePromise.cancel(), 1000);
cancellablePromise.then(
  () => console.log("Success"), 
  () => console.log("Error or cancelled")
);

0 голосов
/ 23 апреля 2020

Зачем вам нужно отменить цепочку asyn c / await? Может быть, концепция могла бы быть лучше? Что вы думаете об этой идее?

/** Throw new Error() in one of the following
 * call() will stop the execution and go the
 * catch block
 */
try {
    await call1();
    await call2();
    await call3();
} catch (e) {
  console.error(e.message);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...