2015-02-27 7 views
0

В классе qooxdoo, у меня есть набор асинхронных методов, которые должны быть соединены (сериализованным):Использование Обещания в приложении qooxdoo

main: function() { 
    async1(); 
}, 

async1: function() { 
    var json = new qx.data.store.Json("http://..."); 
    json.addListener("loaded", function(event) { 
     ... 
     async2(); 
    }); 
}, 

async2: function() { 
    // similar to async1 
} 

Поскольку число шагов растет, цепь становится трудно trace, и код становится нечитаемым. Что мы можем сделать, это переписать код с помощью Promises:

main: function() { 
    new Promise(this.async1) 
    .then(function() { 
     return new Promise(this.async2); 
    }).then(function() { 
     return new Promise(this.async3); 
    }).catch(...); 
}, 

async1: function(resolve, reject) { 
    var json = new qx.data.store.Json("http://..."); 
    json.addListener("loaded", function(event) { 
     ... 
     resolve(); 
    }); 
}, 

async2: function(resolve, reject) { 
    // similar to async1 
} 

Это прекрасно работает, но только до тех пор, пока добавить некоторую реальную логику. Помните, что это класс qooxdoo с большим количеством материалов, инкапсулированных в this. Но вдруг оказывается, что оба метода async * и анонимные функции, используемые в then() и catch(), имеют свой this контекст, привязанный к window, глобальному объекту. Для того, чтобы иметь возможность использовать фактические this, мы можем выполнить пересвязывание:

main: function() { 
    new Promise(this.async1.bind(this)) 
    .then(function() { 
     this.doSomeStuff(); 
     return new Promise(this.async2.bind(this)); 
    }.bind(this)).then(function() { 
     return new Promise(this.async3.bind(this)); 
    }.bind(this)).then(function() { 
     this.doSomeFinalStuff(); 
    }.bind(this)).catch(function() { 
     this.doStuffOnError(); 
    }.bind(this)); 
}, 

async1: function(resolve, reject) { 
    this.doOtherStuff(); 
    var json = new qx.data.store.Json("http://..."); 
    json.addListener("loaded", function(event) { 
     ... 
     resolve(); 
    }); 
} 

Это, наконец, работает, но Doh, некрасиво это код! Есть ли способы избавиться от этих ручных привязок? Почему не неявно связаны при ссылке this.async1, метод экземпляра?

+0

Просто наблюдение: если вам нужно вызвать длинную цепочку функций, независимо от того, асинхронны они или нет, я думаю, что есть хороший шанс, что в вашем общем дизайне что-то не так. (Возможно, я ошибаюсь, конечно, я не видел код.) Что касается функций, которые не связаны по умолчанию: это означает, что значение по умолчанию предотвратит прохождение несвязанных функций вокруг - необходимое для функционального стиля программирования - нет способ un-bind функции. И, кстати, вам нужен последний вызов 'bind()' выше, если вы используете переменную 'self', как это предлагается в принятом ответе? – user625488

+0

Я видел необходимость такой последовательности асинхронных вызовов, когда вам нужно несколько ресурсов, предоставленных сервером для выполнения операции. Я бы сделал это, чтобы упаковать всю логику в одну функцию, которую я передал бы как обратный вызов для всего, что нужно получить асинхронно. Эта функция сначала проверит, соблюдены ли все предварительные условия, затем выполните все вычисления. IMO, если возможно, переключение на такую ​​структуру кода значительно улучшило бы удобочитаемость - вместо того, чтобы иметь связанную с ним логику, опрыскиваемую множеством функций, у вас есть все в одном месте. – user625488

+0

Кроме того, вы можете использовать структуру, основанную на событиях, вместо того, чтобы передавать обратные вызовы, у вас есть обратные вызовы, в которых вы регистрируете обработчик событий. Это все равно улучшит структуру кода, ИМО. С помощью простых обещаний вы упускаете возможность использовать описательные имена функций для документирования намерений. С событиями вы получаете две возможности для документации: имя события и имя обработчика. – user625488

ответ

1

Проверить this answer out

В принципе, это не выглядит, как вы» делать что-то неправильно в корне. Вот как должен работать этот контекст. Вам действительно нужен какой-то синтаксический сахар для вашего конкретного случая использования. Вы можете сделать эти расширения сахара самостоятельно или использовать библиотеку, чтобы сделать тяжелый подъем.

Вы также можете перестроить свой код, как это предлагает Danbopes, и организовать его, чтобы вы делали меньше встроенных привязок. Это позволит вам лучше читать поток.

1

Если добавить var self = this; в верхней части, в пределах всех функций, вы можете обратиться к самому объекту, вызывающему себя:

main: function() { 
    var self = this; 
    new Promise(self.async1) 
    .then(function() { 
     return new Promise(self.async2); 
    }).then(function() { 
     return new Promise(self.async3); 
    }).catch(...); 
}, 
+0

К сожалению, это освободит нас только от половины связок (для анонимных функций). async * методы будут по-прежнему иметь свои контексты 'this', которые будут отскакивать, иначе он будет по умолчанию' window'. –