@TheToolBox ha una buona risposta per voi.
Solo per divertimento, ho intenzione di mostrarvi una tecnica alternativa che utilizza i generatori che trae ispirazione da coroutines.
Promise.prototype.bind = Promise.prototype.then;
const coro = g => {
const next = x => {
let {done, value} = g.next(x);
return done ? value : value.bind(next);
}
return next();
}
Usando questo, il codice sarà simile a questa
const addElement = elementText =>
new Promise(resolve => {
setTimeout(() => {
var element = document.createElement('H1');
element.innerText = `${elementText} ${Date.now()}`;
document.body.appendChild(element);
resolve();
}, Math.random() * 2000);
});
coro(function*() {
yield addElement('first');
yield addElement('second');
yield addElement('third');
yield addElement('fourth');
}());
Ci sono alcune cose molto interessanti che si possono fare con i generatori di promesse. Non sono immediatamente evidenti qui perché la tua promessa addElement
non risolve alcun valore effettivo.
Se effettivamente resolve
alcuni valori, si potrebbe fare qualcosa di simile
// sync
const appendChild = (x,y) => x.appendChild(y);
// sync
const createH1 = text => {
var elem = document.createElement('h1');
elem.innerText = `${text} ${Date.now()}`;
return elem;
};
// async
const delay = f =>
new Promise(resolve => {
setTimeout(() => resolve(f()), Math.random() * 2000);
});
// create generator; this time it has a name and accepts an argument
// mix and match sync/async as needed
function* renderHeadings(target) {
appendChild(target, yield delay(() => createH1('first')));
appendChild(target, yield delay(() => createH1('second')));
appendChild(target, yield delay(() => createH1('third')));
appendChild(target, yield delay(() => createH1('fourth')));
}
// run the generator; set target to document.body
coro(renderHeadings(document.body));
Degne di nota, createH1
e appendChild
sono funzioni sincrone. Questo approccio consente effettivamente di concatenare le normali funzioni e sfocare le linee tra ciò che è sincronizzato e ciò che è asincrono. Esegue anche/si comporta esattamente come il codice che hai originariamente pubblicato.
Quindi sì, questo ultimo esempio di codice potrebbe essere leggermente più interessante.
Infine,
Un vantaggio del coroutine ha sul .then
concatenamento, è che tutte le promesse risolti possibile accedere all'interno della stessa portata.
Confronta .then
catene ...
op1()
.then(x => op2(x))
.then(y => op3(y)) // cannot read x here
.then(z => lastOp(z)) // cannot read x or y here
al coroutine ...
function*() {
let x = yield op1(); // can read x
let y = yield op2(); // can read x and y here
let z = yield op3(); // can read x, y, and z here
lastOp([x,y,z]); // use all 3 values !
}
Naturalmente ci sono workarounds per questo che utilizzano le promesse, ma oh ragazzo vuol ottenere brutto veloce ...
Se sei interessato a utilizzare i generatori in questo modo, consiglio vivamente di dare un'occhiata al progetto co.
Ed ecco un articolo, Callbacks vs Coroutines, dal creatore di co, @tj.
In ogni caso, spero che hai avuto divertente l'apprendimento di alcune altre tecniche ^__^
le funzioni direzionali potrebbero essere semplificate - '.then (x => addElement ("secondo"))' - Allo stesso modo si potrebbero utilizzare le funzioni di direzione in 'addElement' - ma io non sono sicuro perché si pensa stai creando "una grande quantità di nuove chiusure" –
Ho incontrato anche questo problema e ho finito con l'uso di 'bind', anche se sembra altrettanto disordinato (ma evita i wrapper di funzioni extra):' .then (addElement.bind (null, "second")) ', ecc. –
Basta chiedersi se ci sono oggetti promessi ridondanti creati qui. Qualcosa come te crea 6 oggetti di promessa quando 3 sarebbe sufficiente? Quindi crea già un oggetto promesso che non puoi riutilizzare? Fammi pensare che potrei sbagliarmi. – Nishant