javascript - node - callback to promise bluebird
Forma correcta de escribir bucles para promesas. (10)
No creo que garantice el orden de llamar a logger.log (res);
En realidad, lo hace. Esa declaración se ejecuta antes de la llamada de resolve
.
¿Alguna sugerencia?
Un montón. Lo más importante es tu uso del antipatrón de crear-prometer-manualmente , solo hazlo
promiseWhile(…, function() {
return db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
});
})…
Segundo, eso while
que la función podría simplificarse mucho:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
En tercer lugar, no usaría un ciclo while (con una variable de cierre) sino un ciclo for
:
var promiseFor = Promise.method(function(condition, action, value) {
if (!condition(value)) return value;
return action(value).then(promiseFor.bind(null, condition, action));
});
promiseFor(function(count) {
return count < 10;
}, function(count) {
return db.getUser(email)
.then(function(res) {
logger.log(res);
return ++count;
});
}, 0).then(console.log.bind(console, ''all done''));
¿Cómo construir correctamente un ciclo para asegurarse de que la siguiente llamada promisoria y el archivo logger.log (res) encadenados se ejecuten de forma sincronizada a través de la iteración? (azulejo)
db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise
Intenté de la siguiente manera (método de http://blog.victorquinn.com/javascript-promise-while-loop )
var Promise = require(''bluebird'');
var promiseWhile = function(condition, action) {
var resolver = Promise.defer();
var loop = function() {
if (!condition()) return resolver.resolve();
return Promise.cast(action())
.then(loop)
.catch(resolver.reject);
};
process.nextTick(loop);
return resolver.promise;
});
var count = 0;
promiseWhile(function() {
return count < 10;
}, function() {
return new Promise(function(resolve, reject) {
db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
resolve();
});
});
}).then(function() {
console.log(''all done'');
});
Aunque parece funcionar, pero no creo que garantice el orden de llamar a logger.log (res);
¿Alguna sugerencia?
¿Qué tal este utilizando BlueBird ?
function fetchUserDetails(arr) {
return Promise.each(arr, function(email) {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}
Aquí hay otro método (ES6 w / std Promise). Utiliza los criterios de salida del tipo lodash / underscore (return === false). Tenga en cuenta que puede agregar fácilmente un método exitIf () en las opciones para ejecutar en doOne ().
const whilePromise = (fnReturningPromise,options = {}) => {
// loop until fnReturningPromise() === false
// options.delay - setTimeout ms (set to 0 for 1 tick to make non-blocking)
return new Promise((resolve,reject) => {
const doOne = () => {
fnReturningPromise()
.then((...args) => {
if (args.length && args[0] === false) {
resolve(...args);
} else {
iterate();
}
})
};
const iterate = () => {
if (options.delay !== undefined) {
setTimeout(doOne,options.delay);
} else {
doOne();
}
}
Promise.resolve()
.then(iterate)
.catch(reject)
})
};
Así es como lo hago con el objeto Promesa estándar.
// Given async function sayHi
function sayHi() {
return new Promise((resolve) => {
setTimeout(() => {
console.log(''Hi'');
resolve();
}, 3000);
});
}
// And an array of async functions to loop through
const asyncArray = [sayHi, sayHi, sayHi];
// We create the start of a promise chain
let chain = Promise.resolve();
// And append each function in the array to the promise chain
for (const func of asyncArray) {
chain = chain.then(func);
}
// Output:
// Hi
// Hi (After 3 seconds)
// Hi (After 3 more seconds)
Haría algo como esto:
var request = []
while(count<10){
request.push(db.getUser(email).then(function(res) { return res; }));
count++
};
Promise.all(request).then((dataAll)=>{
for (var i = 0; i < dataAll.length; i++) {
logger.log(dataAll[i]);
}
});
de esta manera, dataAll es una matriz ordenada de todos los elementos para el registro. Y la operación de registro funcionará cuando se cumplan todas las promesas.
Hay una nueva forma de resolver esto y es mediante el uso de async / await.
async function myFunction() {
while(/* my condition */) {
const res = await db.getUser(email);
logger.log(res);
}
}
myFunction().then(() => {
/* do other stuff */
})
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function https://ponyfoo.com/articles/understanding-javascript-async-await
La función sugerida por Bergi es realmente agradable:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Aún así, quiero hacer una pequeña adición, lo que tiene sentido, cuando se usan promesas:
var promiseWhile = Promise.method(function(condition, action, lastValue) {
if (!condition()) return lastValue;
return action().then(promiseWhile.bind(null, condition, action));
});
De esta forma, el ciclo while puede insertarse en una cadena de promesas y resuelve con lastValue (también si la acción () nunca se ejecuta). Ver ejemplo:
var count = 10;
util.promiseWhile(
function condition() {
return count > 0;
},
function action() {
return new Promise(function(resolve, reject) {
count = count - 1;
resolve(count)
})
},
count)
Si realmente quiere una promiseWhen()
general promiseWhen()
funcione para este y otros fines, entonces hágalo de todos modos, utilizando las simplificaciones de Bergi. Sin embargo, debido a la forma en que funcionan las promesas, pasar devoluciones de llamada de esta manera es generalmente innecesario y te obliga a saltar a través de pequeños aros complejos.
Por lo que puedo decir, estás intentando:
- para obtener asincrónicamente una serie de detalles de usuario para una colección de direcciones de correo electrónico (al menos, ese es el único escenario que tiene sentido).
- para hacerlo, construyendo una cadena
.then()
través de la recursión. - para mantener el orden original al manejar los resultados devueltos.
Definido así, el problema es en realidad el que se analiza en "The Collection Kerfuffle" en Promise Anti-patterns , que ofrece dos soluciones simples:
- llamadas asincrónicas paralelas usando
Array.prototype.map()
- llamadas asincrónicas en serie usando
Array.prototype.reduce()
.
El enfoque paralelo proporcionará (de manera directa) el problema que intenta evitar: que el orden de las respuestas sea incierto. El enfoque en serie construirá la cadena .then()
requerida - plana - sin recurrencia.
function fetchUserDetails(arr) {
return arr.reduce(function(promise, email) {
return promise.then(function() {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}, Promise.resolve());
}
Llame de la siguiente manera:
//Compose here, by whatever means, an array of email addresses.
var arrayOfEmailAddys = [...];
fetchUserDetails(arrayOfEmailAddys).then(function() {
console.log(''all done'');
});
Como puede ver, no hay necesidad de contar la varita externa fea o su función de condition
asociada. El límite (de 10 en la pregunta) está completamente determinado por la longitud de la matriz arrayOfEmailAddys
.
Dado
- función asyncFn
- conjunto de elementos
Necesario
- Promete encadenar .then () en serie (en orden)
- es6 nativo
Solución
let asyncFn = (item) => {
return new Promise((resolve, reject) => {
setTimeout( () => {console.log(item); resolve(true)}, 1000 )
})
}
// asyncFn(''a'')
// .then(()=>{return async(''b'')})
// .then(()=>{return async(''c'')})
// .then(()=>{return async(''d'')})
let a = [''a'',''b'',''c'',''d'']
a.reduce((previous, current, index, array) => {
return previous // initiates the promise chain
.then(()=>{return asyncFn(array[index])}) //adds .then() promise for each item
}, Promise.resolve())
function promiseLoop(promiseFunc, paramsGetter, conditionChecker, eachFunc, delay) {
function callNext() {
return promiseFunc.apply(null, paramsGetter())
.then(eachFunc)
}
function loop(promise, fn) {
if (delay) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, delay);
})
.then(function() {
return promise
.then(fn)
.then(function(condition) {
if (!condition) {
return true;
}
return loop(callNext(), fn)
})
});
}
return promise
.then(fn)
.then(function(condition) {
if (!condition) {
return true;
}
return loop(callNext(), fn)
})
}
return loop(callNext(), conditionChecker);
}
function makeRequest(param) {
return new Promise(function(resolve, reject) {
var req = https.request(function(res) {
var data = '''';
res.on(''data'', function (chunk) {
data += chunk;
});
res.on(''end'', function () {
resolve(data);
});
});
req.on(''error'', function(e) {
reject(e);
});
req.write(param);
req.end();
})
}
function getSomething() {
var param = 0;
var limit = 10;
var results = [];
function paramGetter() {
return [param];
}
function conditionChecker() {
return param <= limit;
}
function callback(result) {
results.push(result);
param++;
}
return promiseLoop(makeRequest, paramGetter, conditionChecker, callback)
.then(function() {
return results;
});
}
getSomething().then(function(res) {
console.log(''results'', res);
}).catch(function(err) {
console.log(''some error along the way'', err);
});