Sementara loop dengan janji-janji

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

Mengomentari pertanyaan (4)

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 
Komentar (7)

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.

Komentar (2)
Larutan

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');
});
Komentar (4)

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);
})
Komentar (3)

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:

  1. Membuat sebuah instance baru dari sebuah tangguhan janji
  2. Menentukan fungsi yang ingin anda panggil dalam loop
  3. Di dalam fungsi itu:
  4. Periksa untuk melihat apakah anda're dilakukan; dan ketika anda menyelesaikan janji yang dibuat di #1 dan mengembalikannya.
  5. Jika anda tidak dilakukan maka katakan Q untuk menggunakan yang sudah ada janji dan menjalankan unfullfilled fungsi itu adalah "rekursif" fungsi, atau gagal jika itu meninggal. Q. ketika(janji, yourFunction, failFunction)
  6. Setelah mendefinisikan fungsi anda gunakan Q untuk memicu fungsi untuk pertama kalinya menggunakan Q. nextTick(yourFunction)
  7. Akhirnya kembali janji baru ke pemanggil (yang akan memicu seluruh hal untuk memulai).

Stuart's jawaban untuk yang lebih generik solusi, tapi dasar-dasar yang mengagumkan (setelah anda menyadari bagaimana cara kerjanya).

Komentar (0)

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');
});
Komentar (1)

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);
});
Komentar (0)

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);
})
Komentar (0)
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())
Komentar (1)

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.

Komentar (0)

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]

Komentar (2)

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
Komentar (0)

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.
});

https://github.com/CascadeEnergy/promise-seedloop

Komentar (0)