Робота з пірамідою зворотного виклику node.js


9

Я щойно почав використовувати вузол, і одне, що я швидко помітив, це те, як швидко можуть бути зворотні виклики до глупого рівня відступу:

doStuff(arg1, arg2, function(err, result) {
    doMoreStuff(arg3, arg4, function(err, result) {
        doEvenMoreStuff(arg5, arg6, function(err, result) {
            omgHowDidIGetHere();
        });
    });
});

В офіційному посібнику зі стилю йдеться про те, щоб поставити кожен зворотний виклик в окрему функцію, але це видається занадто обмежувальним у використанні закриттів і зробити один об'єкт, оголошений на верхньому рівні, доступним на кілька шарів вниз, оскільки об'єкт повинен бути переданий через усі проміжні зворотні виклики.

Чи нормально використовувати область функцій, щоб допомогти тут? Помістити всі функції зворотного виклику, яким потрібен доступ до глобально-об’єктного об'єкта, всередині функції, яка оголошує цей об'єкт, щоб він перейшов до закриття?

function topLevelFunction(globalishObject, callback) {

    function doMoreStuffImpl(err, result) {
        doMoreStuff(arg5, arg6, function(err, result) {
            callback(null, globalishObject);
        });
    }

    doStuff(arg1, arg2, doMoreStuffImpl);
}

і так далі для ще декількох шарів ...

Або існують рамки тощо, які допомагають знизити рівні відступи, не оголошуючи названу функцію для кожного зворотного виклику? Як ви маєте справу з пірамідою зворотного дзвінка?


2
Зверніть увагу на додаткову проблему із закриттями - у JS closure фіксується весь батьківський контекст (іншими мовами він фіксує лише використану змінну або ті, які користувач спеціально запитував), спричиняючи приємні витоки пам'яті, якщо ієрархія зворотного виклику є досить глибокою, і якщо, наприклад, десь зберігається зворотний виклик.
Євген

Відповіді:


7

Обіцяння забезпечують чітке розділення проблем між асинхронною поведінкою та інтерфейсом, тому асинхронні функції можна викликати без зворотних викликів, а взаємодія зворотних дзвінків може здійснюватися на загальному інтерфейсі з обіцянками.

Існує кілька реалізацій "обіцянок":


Наприклад, ви можете переписати цей вкладений зворотний зв'язок

http.get(url.parse("http://test.com/"), function(res) {
    console.log(res.statusCode);
    http.get(url.parse(res.headers["location"]), function(res) {
        console.log(res.statusCode);
    });
});

подібно до

httpGet(url.parse("http://test.com/")).then(function (res) {
    console.log(res.statusCode);
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);
});

Замість зворотного дзвінка у зворотному дзвінку a(b(c()))ви ланцюжок ".then" a().then(b()).then(c()).


Вступ тут: http://howtonode.org/promises


Ви б не хотіли пояснити докладніше, чим займаються ці ресурси, і чому ви рекомендуєте їх відповідати на поставлене запитання? "Відповіді лише на посилання" не дуже вітаються на Stack Exchange
gnat

1
Добре вибач. Я додав приклад та більше інформації.
Fabien Sa

3

В якості альтернативи обіцянкам слід поглянути на yieldключове слово в поєднанні з функціями генератора, які будуть введені в EcmaScript 6. Обидва доступні сьогодні в Node.js 0.11.x складає, але вимагає додатково вказати --harmonyпрапор під час роботи Node .js:

$ node --harmony app.js

Використовуючи ці конструкції і бібліотеки , такі як TJ Holowaychuk в співробітництві дозволяє писати асинхронний код в стилі , який виглядає як синхронний код, хоча він по- , як і раніше працює в асинхронному режимі. В основному, ці речі разом реалізують спільну підтримку для Node.js.

В основному, що вам потрібно зробити, це написати функцію генератора для коду, який виконує асинхронний матеріал, зателефонувати в асинхронний матеріал, але приставте його до yieldключового слова. Отже, зрештою, ваш код виглядає так:

var run = function * () {
  var data = yield doSomethingAsync();
  console.log(data);
};

Для запуску цієї генераторної функції вам потрібна така бібліотека, як вищезгадане co. Потім дзвінок виглядає так:

co(run);

Або, кажучи про це:

co(function * () {
  // ...
});

Зауважте, що з функцій генератора ви можете викликати інші функції генератора, все, що вам потрібно зробити, - це yieldповторно встановити їх .

Щоб ознайомитись із цією темою, знайдіть у Google такі терміни, як, yield generators es6 async nodejsі ви повинні знайти тонни інформації. Щоб звикнути до цього, потрібен якийсь час, але як тільки ви його отримаєте, ви не хочете повертатися ніколи.

Зверніть увагу, що це не лише забезпечує вам приємніший синтаксис для виклику функцій, але також дозволяє використовувати звичайні (синхронні) елементи логіки потоку управління, такі як forпетлі або try/ catch. Більше не возиться з великою кількістю зворотних дзвінків і з усіма цими речами.

Хай щастить :-)!


0

Тепер у вас є пакет asyncawait , з дуже близької синтаксису до того , що повинно бути майбутнє нативная підтримка з await& asyncв вузлі.

В основному це дозволяє вам писати асинхронний код, виглядаючи синхронно , різко знижуючи рівень LOC та відступ, з невеликою втратою продуктивності (кількість власників пакету - швидкість 79% порівняно з необмеженими зворотними зворотами), сподіваємось зменшити, коли наявна підтримка буде доступна.

Все-таки хороший вибір для виходу із зворотного виклику / піраміди приреченого кошмару IMO, коли продуктивність не є першочерговою проблемою і коли синхронний стиль письма краще відповідає потребам вашого проекту.

Основний приклад пакету doc:

var foo = async (function() {
    var resultA = await (firstAsyncCall());
    var resultB = await (secondAsyncCallUsing(resultA));
    var resultC = await (thirdAsyncCallUsing(resultB));
    return doSomethingWith(resultC);
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.