2016-05-09 23 views
12

Jestem z Node.js i TypeScript i używam async/await. To jest mój przypadek testowy:Limit czasu w async/poczekaj na

async function doSomethingInSeries() { 
    const res1 = await callApi(); 
    const res2 = await persistInDB(res1); 
    const res3 = await doHeavyComputation(res1); 
    return 'simle'; 
} 

Chciałbym ustawić limit czasu dla ogólnej funkcji. To znaczy. jeśli res1 trwa 2 sekundy, res2 trwa 0,5 sekundy, res3 trwa 5 sekund Chciałbym mieć timeout, że po 3 sekundach pozwolę sobie rzucić błąd.

Przy normalnym setTimeout rozmowy jest problemem, ponieważ zakres jest stracone:

async function doSomethingInSeries() { 
    const timerId = setTimeout(function() { 
     throw new Error('timeout'); 
    }); 

    const res1 = await callApi(); 
    const res2 = await persistInDB(res1); 
    const res3 = await doHeavyComputation(res1); 

    clearTimeout(timerId); 

    return 'simle'; 
} 

I nie mogę złapać go z normalnego Promise.catch:

doSomethingInSeries().catch(function(err) { 
    // errors in res1, res2, res3 will be catched here 
    // but the setTimeout thing is not!! 
}); 

Wszelkie pomysły, jak rozwiązać?

+0

Czy używasz konkretnej biblioteki obietnicy? – Bergi

+0

Nie, tylko standardowa obietnica. – nkint

+0

Więc 2 + 0,5 + 5 + 3 powoduje, że czas oczekiwania wynosi 11,5 sekundy? – Bergi

Odpowiedz

19

Można użyć Promise.race aby timeout:

Promise.race([ 
    doSomethingInSeries(), 
    new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 11.5e3)) 
]).catch(function(err) { 
    // errors in res1, res2, res3 and the timeout will be caught here 
}) 

nie można używać setTimeout bez owijania go w obietnicy.

+0

Mam to samo rozwiązanie, dzięki! – nkint

+0

Dlaczego nie potrzebujesz 'clearTiimeout'? – nkint

+0

@nkint: Nie musimy usuwać go, ponieważ odrzucenie nie jest brane pod uwagę, gdy 'doSomethingInSeries()' ustala się jako pierwszy. Moglibyśmy to wyjaśnić, ale byłoby to dość skomplikowane (optymalnie "wyścig" miałby sposób na anulowanie wolniejszych obietnic, ale nie jest to możliwe w przypadku rodzimych obietnic). – Bergi

2

Ok znalazłem ten sposób:

async function _doSomethingInSeries() { 
    const res1 = await callApi(); 
    const res2 = await persistInDB(res1); 
    const res3 = await doHeavyComputation(res1); 
    return 'simle'; 
} 

async function doSomethingInSeries(): Promise<any> { 
    let timeoutId; 

    const delay = new Promise(function(resolve, reject){ 
    timeoutId = setTimeout(function(){ 
     reject(new Error('timeout')); 
    }, 1000); 
    }); 

    // overall timeout 
    return Promise.race([delay, _doSomethingInSeries()]) 
    .then((res) => { 
     clearTimeout(timeoutId); 
     return res; 
    }); 

} 

błędy ktoś?

To, co pachnie nieco dla mnie, polega na tym, że wykorzystanie obietnic jako strategii asynchronicznej spowoduje wysłanie nam zbyt wielu obiektów, których potrzebuje inna strategia, ale nie jest to temat.

+1

Nie usuwasz przekroczenia limitu czasu, jeśli '_doSomethingInSeries()' kończy się niepowodzeniem. Powinieneś użyć polecenia 'try {return await Promise.race (...); } finally {clearTimeout (timeoutId); } ' – Bergi