2015-11-21 10 views
5

Sto tentando di eseguire una suite di test per un oggetto che restituisce una promessa. Voglio concatenare diverse azioni insieme a brevi timeout tra di loro. Ho pensato che un "quindi" call che ha restituito una promessa avrebbe aspettato che la promessa si fosse adempiuta prima di sparare alla successiva incatenata chiamata.Come si fa a impostare setTimeout in una promessa

ho creato una funzione

function promiseTimeout (time) { 
    return new Promise(function(resolve,reject){ 
    setTimeout(function(){resolve(time);},time); 
    }); 
}; 

per cercare di avvolgere setTimeout in una promessa.

Poi nella mia suite di test, io chiamo qualcosa di simile ...

it('should restore state when browser back button is used',function(done){ 
     r.domOK().then(function(){ 
     xh.fire('akc-route-change','/user/4/profile/new'); 
     }).then(promiseTimeout(2000)).then(function(t){ 
     xu.fire('akc-route-change','/user/6'); 
     }).then(promiseTimeout(10)).then(function(t){ 
     expect(xu.params[0]).to.equal(6); 
     history.back(); 
     }).then(promiseTimeout(10)).then(function(){ 
     expect(xu.params[0]).to.equal(4); 
     done(); 
     }); 
    }); 

posso mettere un punto di interruzione sulla prima xh.fire chiamata e un secondo sulla chiamata xu.fire e sarebbe aspettato due secondo spazio quando continua dal primo punto di interruzione al secondo.

Invece raggiunge immediatamente il secondo punto di interruzione e il valore di t in quel punto non è definito.

Cosa sto sbagliando?

+0

Quello che stai facendo è simile a 'setTimeout (fn(), 1000) 'invece di' setTimeout (fn, 1000) ', ciò che' then' takes è una funzione che restituisce una promessa e non una promessa. –

+0

Inoltre, puoi 'return' promette da' it', non c'è bisogno di usare 'done' –

+0

Non capisco cosa intendi Ho appena cambiato la chiamata setTimout a' setTimeout (risoluzione, ora, tempo); 'ma ciò non non cambiare nulla – akc42

risposta

10

TL; DR - hai avvolto setTimeout in una promessa correttamente, il problema è che si sta utilizzando in modo improprio

.then(promiseTimeout(2000)).then 

non farà quello che ci si aspetta. La "firma" per .then è then(functionResolved, functionRejected)

allora il metodo accetta due argomenti è una promessa:

promise.then (onFulfilled, onRejected)

Entrambi gli argomenti opzionali onFulfilled e onRejected sono:

  • Se onFulfilled non è una funzione, deve essere ignorata.
  • Se onRejected non è una funzione, deve essere ignorato.

fonte: https://promisesaplus.com/#point-21

Non sta passando una funzione per poi

consideri il modo in cui si stanno facendo:

Promise.resolve('hello') 
.then(promiseTimeout(2000)) 
.then(console.log.bind(console)) 

vs come dovrebbe essere fatto:

Promise.resolve('hello').then(function() { 
    return promiseTimeout(2000) 
}).then(console.log.bind(console)) 

Le prime uscite 'ciao' immediatamente

Le seconde uscite 2000 dopo 2 secondi

Pertanto, si dovrebbe fare:

it('should restore state when browser back button is used', function(done) { 
    r.domOK().then(function() { 
     xh.fire('akc-route-change', '/user/4/profile/new'); 
    }).then(function() { 
     return promiseTimeout(2000); 
    }).then(function(t) { 
     xu.fire('akc-route-change', '/user/6'); 
    }).then(function() { 
     return promiseTimeout(10); 
    }).then(function(t) { 
     expect(xu.params[0]).to.equal(6); 
     history.back(); 
    }).then(function() { 
     return promiseTimeout(10); 
    }).then(function() { 
     expect(xu.params[0]).to.equal(4); 
     done(); 
    }); 
}); 

alternativa:

it('should restore state when browser back button is used', function(done) { 
    r.domOK().then(function() { 
     xh.fire('akc-route-change', '/user/4/profile/new'); 
    }).then(promiseTimeout.bind(null, 2000) 
    ).then(function(t) { 
     xu.fire('akc-route-change', '/user/6'); 
    }).then(promiseTimeout.bind(null, 10) 
    ).then(function(t) { 
     expect(xu.params[0]).to.equal(6); 
     history.back(); 
    }).then(promiseTimeout.bind(null, 10) 
    }).then(function() { 
     expect(xu.params[0]).to.equal(4); 
     done(); 
    }); 
}); 
+0

Capito. Come il secondo approccio, anche se ho appena provato entrambi ed entrambi ho anche messo in ordine promiseTimeout - come il commento fatto in risposta a Benjamin Gruenbaum – akc42

+0

Sì, questo funziona bene, ma punto minore, nel primo frammento di codice, facendo 'function() {return promise.Timeout (n); } 'ancora e ancora sembra eccessivamente prolisso, vorrebbe solo scrivere' makePromiseTimeout (n) '. –

2

Per effettuare una timeout che funziona come si desidera, scrivere una funzione che richiede un ritardo e restituisce una funzione adatta per il passaggio a then.

function timeout(ms) { 
    return() => new Promise(resolve => setTimeout(resolve, ms)); 
} 

usare in questo modo:

Promise.resolve() . then(timeout(1000)) . then(() => console.log("got here");); 

Tuttavia, è probabile che si vuole accedere al valore risolto della promessa che conduce al timeout. In tal caso, disporre per la funzione creato da timeout() di passare attraverso il valore:

function timeout(ms) { 
    return value => new Promise(resolve => setTimeout(() => resolve(value), ms)); 
} 

usare in questo modo:

Promise.resolve(42) . then(timeout(1000)) . then(value => console.log(value)); 
+0

Non capisco questa risposta. Togliendo la nuova notazione ES6 e rinominando il timeout per promettere Timeout, la prima parte della risposta sembra identica alla mia domanda originale che non funziona. Dov'è la differenza? – akc42

+0

La funzione 'timeout' restituisce una funzione che restituisce una promessa. Il tuo originale restituisce una promessa. "allora" richiede una funzione - farla passare una promessa come faresti non farà nulla. –

+0

Ci scusiamo per il commento precedente - ora capisco cosa stai dicendo. Trovo questa nuova notazione ES6 un po 'difficile da capire senza un'attenta riflessione e traducendola efficacemente in Javascript. Capisco. – akc42

Problemi correlati