@TheToolBox ma dla Ciebie dobrą odpowiedź.
Dla przyjemności pokażę Wam alternatywną technikę wykorzystującą generatory, która czerpie inspirację z 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();
}
Korzystanie że Twój kod będzie wyglądać następująco
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');
}());
Istnieje kilka bardzo ciekawych rzeczy można zrobić za pomocą generatorów z obietnic. Nie są tu od razu widoczne, ponieważ obietnica addElement
nie rozwiązuje żadnych rzeczywistych wartości.
Jeśli rzeczywiście resolve
pewne wartości, można zrobić coś takiego
// 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));
Warto zauważyć, createH1
i appendChild
funkcje są synchroniczne. To podejście skutecznie pozwala łączyć normalne funkcje i zamazywać linie między tym, co jest synchronizowane, a tym, co jest asynchroniczne. Działa również/zachowuje się dokładnie tak, jak pierwotnie opublikowany kod.
Tak, ten ostatni przykład kodu może być nieco bardziej interesujący.
Wreszcie
Jeden wyraźną przewagę współprogram ma nad .then
łańcuchowych, jest to, że wszystkie obietnice rozwiązanych można uzyskać wewnątrz tego samego zakresu.
Porównaj .then
łańcuchy ...
op1()
.then(x => op2(x))
.then(y => op3(y)) // cannot read x here
.then(z => lastOp(z)) // cannot read x or y here
do współprogram ...
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 !
}
Oczywiście istnieją workarounds za to za pomocą obietnic, ale oh boy robi się brzydkie szybko ...
Jeśli jesteś zainteresowany wykorzystaniem generatorów w ten sposób, bardzo polecam Ci zrealizowanie projektu co.
A oto artykuł, , od twórcy co, @tj.
W każdym razie, mam nadzieję, że zabawa uczenia się o innych technikach ^__^
twoje funkcje strzałek mogłyby zostać uproszczone - '.Następnie (x => addElement („drugi”))' - podobnie mogło być w użyciu funkcje strzałek w 'addElement' - ale nie jestem pewien, dlaczego myślisz tworzysz "wiele nowych zamknięć" –
Wpadłem również na ten problem i skończyło się na używaniu 'bind' zamiast tego, chociaż wydaje się być tak samo nieprzyzwoite (ale unika się dodatkowych wrapperów funkcji):' .then (addElement.bind (null, "second")) ', itp. –
Zastanawiam się, czy istnieją tutaj nadmiarowe obiekty obietnic. Coś takiego jak Ty stworzysz 6 obiektów obietnic, gdy 3 będzie wystarczające? Czy stworzyłeś już obiekt obietnicy, którego nie możesz użyć ponownie? Pozwól mi pomyśleć, że mogę się mylić. – Nishant