Apa yang akan menjadi idiomatic cara untuk melakukan sesuatu seperti while loop dengan janji-janji. Jadi:
melakukan sesuatu jika kondisi masih berdiri melakukannya lagi ulangi kemudian melakukan sesuatu yang lain.
dosomething.then(possilblydomoresomethings).then(finish)
I've dilakukan dengan cara ini aku bertanya-tanya jika ada yang lebih baik/lebih idomatic cara?
var q = require('q');
var index = 1;
var useless = function(){
var currentIndex = index;
console.log(currentIndex)
var deferred = q.defer();
setTimeout(function(){
if(currentIndex > 10)
deferred.resolve(false);
else deferred.resolve(true);
},500);
return deferred.promise;
}
var control = function(cont){
var deferred = q.defer();
if(cont){
index = index + 1;
useless().then(control).then(function(){
deferred.resolve();
});
}
else deferred.resolve();
return deferred.promise;
}
var chain = useless().then(control).then(function(){console.log('done')});
Output: Satu Dua Tiga Empat Lima Enam Tujuh Delapan Sembilan Sepuluh Sebelas dilakukan
Berikut ini's sebuah fungsi yang dapat digunakan kembali yang saya pikir cukup jelas.
var Q = require("q");
// `condition` is a function that returns a boolean
// `body` is a function that returns a promise
// returns a promise for the completion of the loop
function promiseWhile(condition, body) {
var done = Q.defer();
function loop() {
// When the result of calling `condition` is no longer true, we are
// done.
if (!condition()) return done.resolve();
// Use `when`, in case `body` does not return a promise.
// When it completes loop again otherwise, if it fails, reject the
// done promise
Q.when(body(), loop, done.reject);
}
// Start running the loop in the next tick so that this function is
// completely async. It would be unexpected if `body` was called
// synchronously the first time.
Q.nextTick(loop);
// The promise
return done.promise;
}
// Usage
var index = 1;
promiseWhile(function () { return index <= 11; }, function () {
console.log(index);
index++;
return Q.delay(500); // arbitrary async
}).then(function () {
console.log("done");
}).done();
Ini adalah cara paling sederhana I've ditemukan untuk mengekspresikan pola dasar: anda mendefinisikan sebuah fungsi yang memanggil janji, cek hasilnya, dan kemudian menyebut dirinya lagi atau berakhir.
const doSomething = value =>
new Promise(resolve =>
setTimeout(() => resolve(value >= 5 ? 'ok': 'no'), 1000))
const loop = value =>
doSomething(value).then(result => {
console.log(value)
if (result === 'ok') {
console.log('yay')
} else {
return loop(value + 1)
}
})
loop(1).then(() => console.log('all done!'))
Melihatnya dalam tindakan di JSBin
Jika anda menggunakan sebuah janji yang menyelesaikan atau menolak, anda akan mendefinisikan maka
dan menangkap
bukan menggunakan if-clause.
Jika anda memiliki sebuah array dari janji-janji, anda hanya akan mengubah loop
untuk menggeser atau pop berikutnya setiap waktu.
EDIT: Berikut's versi yang menggunakan async/menanti
, karena itu's 2018:
const loop = async value => {
let result = null
while (result != 'ok') {
console.log(value)
result = await doSomething(value)
value = value + 1
}
console.log('yay')
}
Melihatnya dalam tindakan di CodePen
Seperti yang anda lihat, menggunakan normal while loop dan tidak ada rekursi.
I'a menggunakan sebuah objek untuk membungkus nilai. Dengan cara itu anda dapat memiliki dilakukan
properti untuk membiarkan loop tahu anda'kembali dilakukan.
// fn should return an object like
// {
// done: false,
// value: foo
// }
function loop(promise, fn) {
return promise.then(fn).then(function (wrapper) {
return !wrapper.done ? loop(Q(wrapper.value), fn) : wrapper.value;
});
}
loop(Q.resolve(1), function (i) {
console.log(i);
return {
done: i > 10,
value: i++
};
}).done(function () {
console.log('done');
});
Ini adalah untuk bluebird tidak q tapi karena anda tidak't menyebutkan q khusus.. di bluebird api doc penulis menyebutkan kembali janji-fungsi pembangkit akan lebih idiomatik dari menggunakan deferreds.
var Promise = require('bluebird');
var i = 0;
var counter = Promise.method(function(){
return i++;
})
function getAll(max, results){
var results = results || [];
return counter().then(function(result){
results.push(result);
return (result < max) ? getAll(max, results) : results
})
}
getAll(10).then(function(data){
console.log(data);
})
Karena saya dapat't mengomentari Stuart K's jawaban saya'll menambahkan sedikit di sini. Berdasarkan Stuart K's jawaban anda dapat merebusnya untuk anehnya konsep sederhana: menggunakan Kembali janji yang belum terpenuhi. Apa yang telah ia pada dasarnya adalah:
Stuart's jawaban untuk yang lebih generik solusi, tapi dasar-dasar yang mengagumkan (setelah anda menyadari bagaimana cara kerjanya).
Pola ini sekarang lebih mudah disebut dengan menggunakan q-flow. Contoh, untuk masalah di atas:
var q = require('q');
require('q-flow');
var index = 1;
q.until(function() {
return q.delay(500).then(function() {
console.log(index++);
return index > 10;
});
}).done(function() {
return console.log('done');
});
Berikut ini adalah sebuah ekstensi untuk Janji
prototipe untuk meniru perilaku untuk
loop. Mendukung janji-janji atau langsung nilai-nilai untuk inisialisasi, kondisi, loop tubuh, dan peningkatan bagian-bagian. Ini juga memiliki dukungan penuh untuk pengecualian, dan ia tidak memiliki kebocoran memori. Sebuah contoh diberikan di bawah ini tentang cara menggunakannya.
var Promise = require('promise');
// Promise.loop([properties: object]): Promise()
//
// Execute a loop based on promises. Object 'properties' is an optional
// argument with the following fields:
//
// initialization: function(): Promise() | any, optional
//
// Function executed as part of the initialization of the loop. If
// it returns a promise, the loop will not begin to execute until
// it is resolved.
//
// Any exception occurring in this function will finish the loop
// with a rejected promise. Similarly, if this function returns a
// promise, and this promise is reject, the loop finishes right
// away with a rejected promise.
//
// condition: function(): Promise(result: bool) | bool, optional
//
// Condition evaluated in the beginning of each iteration of the
// loop. The function should return a boolean value, or a promise
// object that resolves with a boolean data value.
//
// Any exception occurring during the evaluation of the condition
// will finish the loop with a rejected promise. Similarly, it this
// function returns a promise, and this promise is rejected, the
// loop finishes right away with a rejected promise.
//
// If no condition function is provided, an infinite loop is
// executed.
//
// body: function(): Promise() | any, optional
//
// Function acting as the body of the loop. If it returns a
// promise, the loop will not proceed until this promise is
// resolved.
//
// Any exception occurring in this function will finish the loop
// with a rejected promise. Similarly, if this function returns a
// promise, and this promise is reject, the loop finishes right
// away with a rejected promise.
//
// increment: function(): Promise() | any, optional
//
// Function executed at the end of each iteration of the loop. If
// it returns a promise, the condition of the loop will not be
// evaluated again until this promise is resolved.
//
// Any exception occurring in this function will finish the loop
// with a rejected promise. Similarly, if this function returns a
// promise, and this promise is reject, the loop finishes right
// away with a rejected promise.
//
Promise.loop = function(properties)
{
// Default values
properties = properties || {};
properties.initialization = properties.initialization || function() { };
properties.condition = properties.condition || function() { return true; };
properties.body = properties.body || function() { };
properties.increment = properties.increment || function() { };
// Start
return new Promise(function(resolve, reject)
{
var runInitialization = function()
{
Promise.resolve().then(function()
{
return properties.initialization();
})
.then(function()
{
process.nextTick(runCondition);
})
.catch(function(error)
{
reject(error);
});
}
var runCondition = function()
{
Promise.resolve().then(function()
{
return properties.condition();
})
.then(function(result)
{
if (result)
process.nextTick(runBody);
else
resolve();
})
.catch(function(error)
{
reject(error);
});
}
var runBody = function()
{
Promise.resolve().then(function()
{
return properties.body();
})
.then(function()
{
process.nextTick(runIncrement);
})
.catch(function(error)
{
reject(error);
});
}
var runIncrement = function()
{
Promise.resolve().then(function()
{
return properties.increment();
})
.then(function()
{
process.nextTick(runCondition);
})
.catch(function(error)
{
reject(error);
});
}
// Start running initialization
process.nextTick(runInitialization);
});
}
// Promise.delay(time: double): Promise()
//
// Returns a promise that resolves after the given delay in seconds.
//
Promise.delay = function(time)
{
return new Promise(function(resolve)
{
setTimeout(resolve, time * 1000);
});
}
// Example
var i;
Promise.loop({
initialization: function()
{
i = 2;
},
condition: function()
{
return i < 6;
},
body: function()
{
// Print "i"
console.log(i);
// Exception when 5 is reached
if (i == 5)
throw Error('Value of "i" reached 5');
// Wait 1 second
return Promise.delay(1);
},
increment: function()
{
i++;
}
})
.then(function()
{
console.log('LOOP FINISHED');
})
.catch(function(error)
{
console.log('EXPECTED ERROR:', error.message);
});
Saya sekarang menggunakan ini:
function each(arr, work) {
function loop(arr, i) {
return new Promise(function(resolve, reject) {
if (i >= arr.length) {resolve();}
else try {
Promise.resolve(work(arr[i], i)).then(function() {
resolve(loop(arr, i+1))
}).catch(reject);
} catch(e) {reject(e);}
});
}
return loop(arr, 0);
}
Ini menerima sebuah array arr
dan fungsi bekerja
dan kembali Janji
. Disediakan fungsi dipanggil sekali untuk masing-masing elemen dalam array dan akan diteruskan elemen saat ini dan itu's indeks dalam array. Mungkin sinkron atau asinkron, yang dalam hal ini harus kembali Janji.
Anda dapat menggunakannya seperti ini:
var items = ['Hello', 'cool', 'world'];
each(items, function(item, idx) {
// this could simply be sync, but can also be async
// in which case it must return a Promise
return new Promise(function(resolve){
// use setTimeout to make this async
setTimeout(function(){
console.info(item, idx);
resolve();
}, 1000);
});
})
.then(function(){
console.info('DONE');
})
.catch(function(error){
console.error('Failed', error);
})
Setiap item dalam array akan ditangani pada gilirannya. Setelah semua ditangani, kode yang diberikan untuk .maka()
akan berjalan, atau, jika beberapa kesalahan yang terjadi, kode yang diberikan untuk .menangkap()
. Dalam bekerja
fungsi, anda dapat membuang
sebuah Kesalahan
(dalam kasus sinkron fungsi) atau menolak
yang Menjanjikan
(dalam kasus async fungsi) untuk membatalkan loop.
function each(arr, work) {
function loop(arr, i) {
return new Promise(function(resolve, reject) {
if (i >= arr.length) {resolve();}
else try {
Promise.resolve(work(arr[i], i)).then(function() {
resolve(loop(arr, i+1))
}).catch(reject);
} catch(e) {reject(e);}
});
}
return loop(arr, 0);
}
var items = ['Hello', 'cool', 'world'];
each(items, function(item, idx) {
// this could simply be sync, but can also be async
// in which case it must return a Promise
return new Promise(function(resolve){
// use setTimeout to make this async
setTimeout(function(){
console.info(item, idx);
resolve();
}, 1000);
});
})
.then(function(){
console.info('DONE');
})
.catch(function(error){
console.error('Failed', error);
})
var Q = require('q')
var vetor = ['a','b','c']
function imprimeValor(elements,initValue,defer){
console.log( elements[initValue++] )
defer.resolve(initValue)
return defer.promise
}
function Qloop(initValue, elements,defer){
Q.when( imprimeValor(elements, initValue, Q.defer()), function(initValue){
if(initValue===elements.length){
defer.resolve()
}else{
defer.resolve( Qloop(initValue,elements, Q.defer()) )
}
}, function(err){
defer.reject(err)
})
return defer.promise
}
Qloop(0, vetor,Q.defer())
Banyak jawaban di sini dan apa yang anda mencoba untuk mencapai sangat tidak praktis. tapi ini harus bekerja. Ini dilaksanakan di sebuah aws lambda function, dengan Node.js 10 ia akan pergi sampai fungsi timeout. Hal ini juga dapat mengkonsumsi jumlah yang layak dari memori.
exports.handler = async (event) => {
let res = null;
while (true) {
try{
res = await dopromise();
}catch(err){
res = err;
}
console.log(res);
}//infinite will time out
};
function dopromise(){
return new Promise((resolve, reject) => {
//do some logic
//if error reject
//reject('failed');
resolve('success');
});
}
Diuji pada lambda dan berjalan baik-baik saja selama lebih dari 5 menit. Tapi seperti yang dinyatakan oleh orang lain ini bukan hal yang baik untuk dilakukan.
Menggunakan ES6 Janji, saya datang dengan ini. Itu rantai janji-janji dan kembali janji. It's tidak secara teknis sebuah while loop, tetapi tidak menunjukkan bagaimana untuk iterate atas janji-janji serentak.
function chain_promises(list, fun) {
return list.reduce(
function (promise, element) {
return promise.then(function () {
// I only needed to kick off some side-effects. If you need to get
// a list back, you would append to it here. Or maybe use
// Array.map instead of Array.reduce.
fun(element);
});
},
// An initial promise just starts things off.
Promise.resolve(true)
);
}
// To test it...
function test_function (element) {
return new Promise(function (pass, _fail) {
console.log('Processing ' + element);
pass(true);
});
}
chain_promises([1, 2, 3, 4, 5], test_function).then(function () {
console.log('Done.');
});
[Di sini's my biola.][1]
Saya pikir saya mungkin juga melemparkan topi ke dalam ring, dengan menggunakan ES6 janji-Janji...
function until_success(executor){
var before_retry = undefined;
var outer_executor = function(succeed, reject){
var rejection_handler = function(err){
if(before_retry){
try {
var pre_retry_result = before_retry(err);
if(pre_retry_result)
return succeed(pre_retry_result);
} catch (pre_retry_error){
return reject(pre_retry_error);
}
}
return new Promise(executor).then(succeed, rejection_handler);
}
return new Promise(executor).then(succeed, rejection_handler);
}
var outer_promise = new Promise(outer_executor);
outer_promise.before_retry = function(func){
before_retry = func;
return outer_promise;
}
return outer_promise;
}
Sang eksekutor
argumen adalah yang sama sebagai yang berlalu untuk sebuah Janji
konstruktor, tapi akan dipanggil berulang-ulang sampai memicu keberhasilan callback. The before_retry
fungsi memungkinkan untuk custom penanganan kesalahan pada gagal mencoba. Jika ia mengembalikan truthy nilai itu akan dianggap sebagai bentuk keberhasilan dan "loop" akan berakhir, dengan truthy sebagai hasilnya. Jika tidak ada before_retry
fungsi terdaftar, atau mengembalikan falsey nilai, maka loop akan dijalankan selama iterasi. Pilihan ketiga adalah bahwa before_retry
fungsi melempar kesalahan itu sendiri. Jika ini terjadi, maka "loop" akan berakhir, melewati kesalahan-kesalahan itu sebagai sebuah kesalahan.
Berikut ini adalah contoh:
var counter = 0;
function task(succ, reject){
setTimeout(function(){
if(++counter < 5)
reject(counter + " is too small!!");
else
succ(counter + " is just right");
}, 500); // simulated async task
}
until_success(task)
.before_retry(function(err){
console.log("failed attempt: " + err);
// Option 0: return falsey value and move on to next attempt
// return
// Option 1: uncomment to get early success..
//if(err === "3 is too small!!")
// return "3 is sort of ok";
// Option 2: uncomment to get complete failure..
//if(err === "3 is too small!!")
// throw "3rd time, very unlucky";
}).then(function(val){
console.log("finally, success: " + val);
}).catch(function(err){
console.log("it didn't end well: " + err);
})
Output untuk pilihan 0:
failed attempt: 1 is too small!!
failed attempt: 2 is too small!!
failed attempt: 3 is too small!!
failed attempt: 4 is too small!!
finally, success: 5 is just right
Output untuk opsi 1:
failed attempt: 1 is too small!!
failed attempt: 2 is too small!!
failed attempt: 3 is too small!!
finally, success: 3 is sort of ok
Output untuk opsi 2:
failed attempt: 1 is too small!!
failed attempt: 2 is too small!!
failed attempt: 3 is too small!!
it didn't end well: 3rd time, very unlucky
Saya menulis sebuah modul yang membantu anda melakukan dirantai loop dari asynchronous tugas dengan janji-janji, hal ini didasarkan pada jawaban di atas disediakan oleh juandopazo
/**
* Should loop over a task function which returns a "wrapper" object
* until wrapper.done is true. A seed value wrapper.seed is propagated to the
* next run of the loop.
*
* todo/maybe? Reject if wrapper is not an object with done and seed keys.
*
* @param {Promise|*} seed
* @param {Function} taskFn
*
* @returns {Promise.<*>}
*/
function seedLoop(seed, taskFn) {
const seedPromise = Promise.resolve(seed);
return seedPromise
.then(taskFn)
.then((wrapper) => {
if (wrapper.done) {
return wrapper.seed;
}
return seedLoop(wrapper.seed, taskFn);
});
}
// A super simple example of counting to ten, which doesn't even
// do anything asynchronous, but if it did, it should resolve to
// a promise that returns the { done, seed } wrapper object for the
// next call of the countToTen task function.
function countToTen(count) {
const done = count > 10;
const seed = done ? count : count + 1;
return {done, seed};
}
seedLoop(1, countToTen).then((result) => {
console.log(result); // 11, the first value which was over 10.
});