Играя с современными JS и немного застрял со следующим.
Рассмотрите возможность использования ExtSystem через некоторый HTTP API и локальный экземпляр Mongo. Оба они содержат объекты с name
и id
.
Для Mongo я использую mongoose
с моделью ObjectSchema
({_id, sourceId, name, internalParam}
), где sourceId
равно id
из ExtSystem и internalParam
существует только в моем приложении. Для ExtSystem существует 2 метода, возвращающих request.js
Promise:
[id, id, id]
{id, name}
Существует также глобальная функция errHandler
которая может обрабатывать ошибки как с request.js
и с mongoose
Promises. Цель состоит в том, чтобы синхронизировать Mongo с ExtSystem: обновить все объекты из ExtSystem в Mongo и удалить больше не в ExtSystem из Mongo.
Что я придумал:
ExtSystem.all().then(body => {
let basket = []; // will store all promises for both ExtSystem and Mongo requests
basket.push(...body.map(o => ExtSystem.get(o.id));
basket.push(ObjectSchema.find({}, 'sourceId'));
Promise.all(basket).then(basketDoc => {
let mongoObjects = {}, extObjects = {};
basketDoc.pop().forEach(o => mongoObjects[o.sourceId] = o._id); // Mongo retuns array of {_id, sourceId } objects
basketDoc.forEach(o => { // ExtSystem returns array of {id, name} objects
extObjects[o.id] = {
sourceId: o.id,
name: o.name
}
});
let esSet = new Set(Object.keys(extObjects));
let mongoDeleteIds = Object.keys(mongoObjects).filter(oId => !esSet.has(oId)); // Set.has is faster than Array.indexOf
let syncPromises = [];
syncPromises.push(...Object.keys(extObjects).map(oId => ObjectSchema.findOneAndUpdate({ sourceId: extObjects[oId].sourceId }, extObjects[oId], { upsert: true, new: true })));
syncPromises.push(...mongoDeleteIds.map(oId => ObjectSchema.remove({_id: oId})));
Promise.all(syncPromises).then(_ => { // I don't need results, only the moment when sync is complete
ObjectSchema.find().then(doc => { // return actual objects from Mongo
someBusinessLogic(doc);
}).catch(errHandler);
}).catch(errHandler);
}).catch(errHandler);
}).catch(errHandler);
Таким образом, у меня все еще есть 4 вложенных Promise, и, вероятно, что-то не хватает. Есть ли лучший способ добиться этого с помощью менее сложного кода?
Обещания направлены на то, чтобы избавиться от пирамид смерти. Если у вас есть вложенные обещания, вы делаете это неправильно.
Обещания позволяют вам вернуть другое обещание внутри вызова, чтобы связать их. Поэтому вместо того, чтобы делать:
p1.then(stuff => {
p2.then(stuff =>{
...
});
});
Ты должен сделать
p1
.then(stuff => {
return p2;
}).then(stuff => {
return;
});
Если у вас есть какие-то переменные, которые вам нужны для доступа в будущих обещаниях, вы можете включить их в качестве другого обещания или использовать этот фрагмент кода, который я создал некоторое время назад, что создает обещание, которое содержит глобальный объект многократного использования.
Promise
.resolve({}) // creates global object for holding values
.then(obj => {
return pack(obj, taskPromiseA, "a", taskPromiseB, "b");
})
.then(obj => { // you can access results from A and B here
return pack(obj, taskPromiseC, "c");
})
.then(console.log); // you can access them all here
Вы можете вернуть Promise из последующего, чтобы связать его. Поскольку это может быть цепочка, это означает, что все ваши ошибки могут быть переданы одному обработчику.
Ваш код может по существу стать:
ExtSystem.all().then(body => {
let basket = []; // will store all promises for both ExtSystem and Mongo requests
basket.push(...body.map(o => ExtSystem.get(o.id));
basket.push(ObjectSchema.find({}, 'sourceId'));
return Promise.all(basket);
}).then(basketDoc => {
let mongoObjects = {}, extObjects = {};
basketDoc.pop().forEach(o => mongoObjects[o.sourceId] = o._id); // Mongo retuns array of {_id, sourceId } objects
basketDoc.forEach(o => { // ExtSystem returns array of {id, name} objects
extObjects[o.id] = {
sourceId: o.id,
name: o.name
}
});
let esSet = new Set(Object.keys(extObjects));
let mongoDeleteIds = Object.keys(mongoObjects).filter(oId => !esSet.has(oId)); // Set.has is faster than Array.indexOf
let syncPromises = [];
syncPromises.push(...Object.keys(extObjects).map(oId => ObjectSchema.findOneAndUpdate({ sourceId: extObjects[oId].sourceId }, extObjects[oId], { upsert: true, new: true })));
syncPromises.push(...mongoDeleteIds.map(oId => ObjectSchema.remove({_id: oId})));
return Promise.all(syncPromises);
}).then(_ => {
return ObjectSchema.find();
}).then(doc => {
return someBusinessLogic(doc);
}).catch(errHandler);