Цепочки обещаний и анонимные возвраты обещаний

Здесь у меня есть цепочка обещаний, которая отлично работает. Все * .destroy - это обещания, которые возвращают обещания:

function callDBDestroy() {
   var db;

   DB_Categories.destroy().then(function () {
      return DB_Equipment.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   }).then(function () {
      return DB_Certificates.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   }).then(function () {
      return DB_Locations.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   });
}

Но я хочу добавить оператор if в каждый из них, чтобы проверить, существует ли база данных PouchDB (чего нет, если DB_ * имеет значение null).

Если он существует, я хочу уничтожить его, а затем вернуть (и все они возвращают обещания).

Если его не существует, я хочу вернуть анонимное обещание, которое ничего не возвращает, поскольку ни одно из обещаний не имеет данных, которые меня интересуют.

В этом примере я добавил некоторый образец кода для выполнения оператора if, и мне было интересно, что бы я поместил в нулевой экземпляр, который передал бы значение обещания (разрешения).

function callDBDestroy() {
   var db;


   DB_Categories.destroy().then(function () {
      if( DB_Equipment != null) {
          return DB_Equipment.destroy();
      }
      else {
          Anonymous empty promise - something like:

          new Promise().resolve();

      }
   }).then(function () {
      return DB_Certificates.destroy();
   }).then(function () {
      return DB_Locations.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   });
}

Спасибо,

Том


person tshad    schedule 20.03.2017    source источник
comment
Предполагая, что вы не захотите продолжить выполнение остальных связанных обещаний, если ваша БД равна нулю, нет?   -  person Rob M.    schedule 21.03.2017
comment
Кроме того, вы можете просто позвонить Promise.resolve(), вам не нужны new вещи   -  person Rob M.    schedule 21.03.2017
comment
Вы имеете в виду методы, возвращающие обещания?   -  person Bergi    schedule 21.03.2017
comment
они должны запускаться последовательно?   -  person Thomas    schedule 21.03.2017
comment
Поскольку похоже, что вы здесь новичок, если какой-либо из приведенных ниже ответов отвечает на ваш вопрос, вы можете сообщить об этом сообществу, щелкнув зеленую галочку рядом с лучшим ответом. Это также принесет вам несколько очков репутации здесь при переполнении стека за соблюдение надлежащей процедуры.   -  person jfriend00    schedule 24.03.2017
comment
В данном случае они входят в серию. Первый набор кода имеет ловушку после каждого .destroy. Я хотел проверить каждую переменную БД, чтобы убедиться, что она равна нулю. Если не null, я могу удалить базу данных. Если нет, я хотел вернуть решение, но эта часть не является обещанием, поэтому я подумал, что мне нужно создать обещание анонимно, чтобы разрешить его.   -  person tshad    schedule 29.03.2017


Ответы (5)


Похоже, вам просто интересно, как вручную разрешить / отклонить обещание. Если это так, вы можете просто вызвать Promise.resolve(optionalValue) или Promise.reject(optionalValue), если хотите перейти к обработчику catch:

function callDBDestroy() {
   var db;

   DB_Categories.destroy()
   .then(function () {
      if( DB_Equipment != null) {
          return DB_Equipment.destroy();
      } else {
          return Promise.resolve();
      }
   }).then(function () {
      return DB_Certificates.destroy();
   }).then(function () {
      return DB_Locations.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   });
}
person Rob M.    schedule 20.03.2017
comment
Нет смысла в return Promise.resolve() из then, поскольку Promise.resolve вызывается автоматически для возвращаемого значения. Можно было бы просто return;. - person Benjamin Gruenbaum; 21.03.2017
comment
@BenjaminGruenbaum Спасибо за записку, согласен. Я пытался объяснить OP, как вручную разрешать / отклонять обещания, поскольку это казалось путаницей. - person Rob M.; 21.03.2017
comment
Это было. Таким образом, решение называется событием, если я вернусь; или вернуть «что-нибудь». - person tshad; 29.03.2017

Вы можете обернуть это:

function withDb(db, handler) {
    return function onFulfilled(value) {
       if(db === null) throw new Error("DB Null");
       return handler(value);
    });
}

Что позволит вам сделать:

function callDBDestroy() {
   var db;
   var w = withDb(db); // or whatever instance

   DB_Categories.destroy().then(w(function () {
       // do stuff

    }))); // .then(w( to chain calls here.
    ...
}
person Benjamin Gruenbaum    schedule 20.03.2017

Я хочу вернуть анонимное обещание, которое ничего не возвращает, поскольку ни одно из обещаний не содержит данных, которые меня интересуют. Что-то вроде:

new Promise().resolve();

Вы ищете Promise.resolve(undefined). Хотя вы можете опустить undefined, это неявно.

….then(function () {
    if (DB_Equipment != null) {
        return DB_Equipment.destroy();
    } else {
        return Promise.resolve(undefined);
    }
}).…

И вам даже не нужно возвращать обещание из обратного вызова then, простой возврат undefined (или не returning) будет иметь тот же эффект.

….then(function () {
    if (DB_Equipment != null) {
        return DB_Equipment.destroy();
    }
}).…

В вашем случае я бы порекомендовал функцию-оболочку:

function destroyDatabase(db, name = "db") {
    if (db != null)
        return db.destroy().catch(err => {
            showMsg(`Error in destroying ${name}: ${err}`);
        });
    else
        return Promise.resolve();
}
function callDBDestroy() {
    return destroyDatabase(DB_Categories, "categories")
    .then(() => destroyDatabase(DB_Certificates, "certificates"))
    .then(() => destroyDatabase(DB_Locations, "locations"))
}
// or even in parallel:
function callDBDestroy() {
    return Promise.all([
        destroyDatabase(DB_Categories, "categories"),
        destroyDatabase(DB_Certificates, "certificates"),
        destroyDatabase(DB_Locations, "locations")
    ]);
}
person Bergi    schedule 20.03.2017
comment
Я думал, вам нужно вернуть деньги, иначе обещания вызовут проблемы (побочные эффекты). Я обнаружил, что в некоторых случаях, когда у меня были проблемы с кодом, добавление возврата решало их. - person tshad; 29.03.2017
comment
Да, если вы делаете что-то, что использует обещания, вам (в значительной степени) всегда нужно return их. Однако, если базы данных нет и вы ничего не делаете, вам также не нужно возвращать никаких результатов. - person Bergi; 29.03.2017

Как насчет использования массива, поскольку вы выполняете ту же задачу, но изменяется только БД:

//serial
function callDBDestroy() {
    var databases = [
        DB_Categories,
        DB_Equipment,
        DB_Certificates,
        DB_Locations
    ];

    function errorMessage(err){ showMsg("Error in callDBDestroy: " + err) };

    databases.reduce(
        (prev, db) => db == null? 
            prev: 
            prev.then(() => db.destroy().catch(errorMessage)), 
        Promise.resolve()
    )
}

//parallel
function callDBDestroy() {
    var databases = [
        DB_Categories,
        DB_Equipment,
        DB_Certificates,
        DB_Locations
    ];

    function errorMessage(err){ showMsg("Error in callDBDestroy: " + err) };

    databases.forEach( db => db && db.destroy().catch(errorMessage) );
}

Я добавил серийную и параллельную версию.

person Thomas    schedule 20.03.2017

Кажется, вы можете DRY это устранить и заменить много избыточного кода, используя массив баз данных, а затем просто прокрутите массив:

function callDbDestroy();
    var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];

    // chain all the destroys together
    return dbs.reduce((p, db) => {
        return p.then(() => {
            if (db) {
                return db.destroy().catch(err => {
                    showMsg("Error in callDBDestroy: " + err);
                });
            }
        });

    }, Promise.resolve());
}

Вам не нужно возвращать обещание от обработчика .then(). Если у вас просто нет возвращаемого значения, это похоже на выполнение return undefined, что означает, что никакое значение не будет передано следующему обработчику .then(), но цепочка обещаний продолжится нормально. По идее, он работает так же, как return Promise.resolve(), но не нужно делать дополнительных обещаний.

Поскольку вы не передаете значение от одного .then() к следующему в цепочке, вам нечего передавать, поэтому вы можете просто не возвращать ничего, если нет значения db для вызова уничтожения.

К вашему сведению, использование .reduce() для цикла по массиву со структурой return p.then(...) является распространенным шаблоном проектирования для последовательности асинхронных операций с массивом.

К вашему сведению, используя библиотеку обещаний Bluebird (в которой есть несколько полезных помощников), это можно сделать следующим образом:

function callDbDestroy();
    var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];

    return Promise.mapSeries(dbs, db => {
        if (db) {
            return db.destroy().catch(err => {
                showMsg("Error in callDBDestroy: " + err);
            });
        }
    });
}

Для получения дополнительной информации о том, почему Bluebird (или другие библиотеки обещаний) по-прежнему полезны даже с ES6, см. Есть ли еще причины использовать библиотеки обещаний, такие как Q или BlueBird, теперь, когда у нас есть обещания ES6?


Поскольку кажется, что все эти базы данных могут быть независимыми, мне интересно, почему вы заставляете их выполнять последовательно. Если их не нужно вводить в последовательность, вы можете сделать это:

function callDbDestroy();
    var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];

    return Promise.all(dbs.map(db => {
        if (db) {
            return db.destroy().catch(err => {
                showMsg("Error in callDBDestroy: " + err);
            });
        }
    }));
}

Поскольку при этом выполняются операции параллельно, у него есть возможность сократить время сквозного выполнения по сравнению со строгой сериализацией.

person jfriend00    schedule 20.03.2017
comment
Я действительно думал об использовании Promise.all, но не знал, как его настроить. Я не уверен, что делает карта. - person tshad; 29.03.2017