Ұшақпен цикл болса

Уәделермен уақытша цикл сияқты нәрсе жасаудың идоматтық тәсілі қандай еді. Мәселен:

бірдеңе істеу егер жағдай әлі де болса, оны қайтадан жасайды қайталаңыз содан кейін тағы бір нәрсе жасаңыз.

dosomething.then(possilblydomoresomethings).then(finish)

Мен мұны осылай жасадым деп ойладым, бұл әлдеқайда жақсы/артық идоматикалық жолдар бар ма?

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

Шығару: 1 2 3 4 5 6 7 8 9 10 11 жасалды

66
Егер сіздің ортаңызда қолайлы және қол жетімді болса, рекурсиядан аулақ бола аласыз; төмендегі жаңартылған жауапты қараңыз.
қосылды автор lawrence, көзі
Менің ойымша, Итерацияны «Taskjs» секілді ешқандай «тоқтату» механизмісіз пайдалануға болады деп ойламаймын.
қосылды автор juandopazo, көзі
«Мен жақсы немесе артық идоматикалық жолдар бар ма?» Деп сұрадым. Жоқ, рекурсиса - бұл жолы.
қосылды автор juandopazo, көзі
Өзіңізге жазба жасамай, оны қалай жасайсыз? Мен сезінгендіктен, қайталанудың өзі әдемі болмады, бірақ мен оны қалай істеуге болатынын біле алмадым. Кез-келген түсініктер?
қосылды автор Grummle, көзі

12 жауаптар

Міне, қайтадан қолданылатын функция бар, ол менің ойымша, өте айқын.

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();
57
қосылды
сондықтан мен оны асинхалық әдістермен аламын, оларды ешқашан синхронды емес циклге қоймаңыз немесе синхрондау кодымен кез келген асинк кодын ешқашан орамаңыз.
қосылды автор PositiveGuy, көзі
Бұл рекурсияға сүйенеді, сонда ол қалай ауқымды болады? 1000 қоңырау шалу кезінде не болады?
қосылды автор vitaly-t, көзі
Бұл тамаша! Мен сіздің RSVP.js мысалыңызды келтірдім: jsfiddle.net/wcW4r/1 Бұл пайдалы болуы мүмкін Ember.js пайдаланушылары.
қосылды автор miguelcobain, көзі
Жаңа нұсқасы көп РСВП идиоматикалық және Promise «орауыштарымен» денесі мен күйінде: jsfiddle.net/wcW4r/3
қосылды автор miguelcobain, көзі
Ерекше, бұл маған қате береді: Қате: ReferenceError: setTimeout флеште анықталмаған (сатушы/q.js: 121: 21)
қосылды автор Rambatino, көзі
Әрбір уәде келесі «іс-қимыл циклінің белгілеуінде» орындалатындықтан, әр иерациядан кейін стек жойылады, сондықтан рекурсиямен (мысалы, жинақтың толып кетуі) типтік мәселелер осы жерде қолданылмайды.
қосылды автор Stuart K, көзі
Бұл ерекше жағдайларды жұтып қоюы мүмкін. Q.fcall (body) функциясын қолдану керек .then (loop, done.reject);
қосылды автор Tzanko Matev, көзі

Бұл негізгі үлгіні білдіретін ең қарапайым әдіс: уәдені шақыратын функцияны анықтайды, оның нәтижесін тексереді, содан соң өзін қайта шақырады немесе тоқтатады.

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!'))

JSB-дегі әрекеттегі қараңыз

Егер сіз шешкен немесе қабылдамайтын уәделерді пайдалансаңыз, if-пунктін пайдаланудың орнына содан кейін және catch анықтауыңыз керек.

Егер сізге уәделер жиынтығы болса, әрқайсысын келесі жылжыту немесе жылжыту үшін loop дегенді өзгерте аласыз.


EDIT: Міне, async/await қолданатын нұсқасы, себебі 2018:

const loop = async value => {
  let result = null
  while (result != 'ok') {
    console.log(value)
    result = await doSomething(value)
    value = value + 1
  }
  console.log('yay')
}

Оны CodePen бойынша әрекетте қараңыз

Көріп отырғаныңыздай, ол қалыпты уақытта циклды және ешқандай рекурсияны қолданады.

17
қосылды
Өте жақсы шешім! Мен мұны әр уәдеге әдіс тізбегі арқылы басқа ауыспалы ауысуға бейімдедім. Үлкен қарапайым мысал үшін рақмет!
қосылды автор ozOli, көзі

Мәнді орау үшін нысанды қолданамын. Осылайша сіз циклды аяқталғанын білу үшін done сипатын ала аласыз.

// 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');
});
16
қосылды
Мен бұл шешімді сәтті қолдандым, тіпті require ('уәде'); кітапханасын пайдалану арқылы да. Мен рекурсивті емес шешімді салсам да, stackoverflow.com/questions/36361827/…
қосылды автор Galder Zamarreño, көзі
Есіңізде болсын, бұл жеткілікті уақыт жұмсалғаннан кейін барлық қол жетімді жадты пайдалануы мүмкін; уәделердің әрбір циклінде сақталған нәрсе бар, кем дегенде Q.
қосылды автор Ashe, көзі
@juandopazo сізге i ++ кодын ++ i түрінде өзгерту керек, әйтпесе «шексіз циклды» аласыз.
қосылды автор PauloASilva, көзі
Аша, сіз жадыдағы мәселеге қатысты нақты бола аласыз ба? Кабельді ме?
қосылды автор lgc_ustc, көзі

Бұл көкбiрлiк емес, бiрақ iшiнен ерекше айтылмаған .. көкбiррi api doc-да автор уәде берушi функцияны қайтару туралы кейiнге қалдырылған материалдарды пайдаланғаннан кейiн идиомәдени болады.

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);
})
12
қосылды
Бұл пайдалы болған, уақытша циклды іске асыру үшін рекурсивті функцияны қолданған. Рахмет.
қосылды автор Steve Kehlet, көзі
Мен мұны көргенде пайдалы деп таптым, бірақ мені қызықтырады (менің қолданылуым үшін - SCAN -мен барлық қызыл кілттерді қайталау), бұл рекурсия үлкен деректер жиынтығы үшін өте көп стек жасайды немесе қажетсіз жадты жұмсайды. Менің ойымша, es6 генераторлары мен қабылдауға тиіс бағыт болуы мүмкін.
қосылды автор wkw, көзі
Бұл мен іздеймін, бірақ шешім табуыма көмектесті.
қосылды автор Gustavo Straube, көзі

Мен Стюарт К-нің жауабына түсініктеме бере алмайтындықтан, мұнда біраз қосып беремін. Стюарт К жауаптылығына сүйене отырып, оны қарапайым тұжырымдамаға қайнатуға болады: Орындалмаған уәдені қайта қолданыңыз . Оның мағынасы бар:

  1. Create a new instance of a deferred promise
  2. Define your function that you want to call in a loop
  3. Inside that function:
    1. Check to see if you're done; and when you are resolve the promise created in #1 and return it.
    2. If you are not done then tell Q to use the existing promise and run the unfullfilled function that is the "recursive" function, or fail if it died. Q.when(promise, yourFunction, failFunction)
  4. After defining your function use Q to trigger the function for the first time using Q.nextTick(yourFunction)
  5. Finally return your new promise to the caller (which will trigger the whole thing to start).

Стюарттың жауабы неғұрлым жалпы шешім болып табылады, бірақ негіздер керемет (бұл қалай жұмыс істейтінін түсінгенде).

5
қосылды

Бұл үлгіні енді q-flow пайдалану арқылы оңай атауға болады. Мәселен, жоғарыда аталған мәселе бойынша:

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');
});
4
қосылды
Бұл тамаша, мұны көк бауырмен жасауға бола ма, сондықтан екі түрлі уәде кітапханаларын бірге пайдаланудың қажеті жоқ па?
қосылды автор Trevor D, көзі

Promise прототипі үшін for циклінің әрекетін имитациялауға арналған кеңейтімдер. Ол инициализациялау, жай-күйі, шілтердің корпусына және қадамдық бөлімдерге уәделерді немесе дереу мәндерді қолдайды. Сондай-ақ, ол ерекше жағдайларды толықтай қолдайды және еске түсіруге болмайды. Мысал төменде қалай пайдалануға болатындығы туралы берілген.

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);
});
3
қосылды

Енді мен оны қолданамын:

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

Бұл arr жиынын және work функциясын қабылдайды және Promise функциясын қайтарады. Берілген функция массивтің әрбір элементі үшін бір рет шақырылады және ағымдық элементті жібереді және оның индексі алапта болады. Ол синхрондалуы мүмкін немесе синхрондалуы мүмкін, бұл жағдайда ол Promise қайтаруға тиіс.

Сіз оны келесідей пайдалана аласыз:

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

Алаптағы әрбір элемент өз кезегінде өңделеді. Барлық өңделгеннен кейін .then() -ге берілген код іске қосылады, немесе егер қандай да бір қате пайда болса, код .catch() -ге берілген. work функциясының ішінде error (синхронды функциялар кезінде) немесе reject > Promise (циклдық функциялар болған жағдайда) циклды тоқтату.

<div class="snippet" data-lang="js" data-hide="true" data-console="true"> <div class="snippet-code snippet-currently-hidden">

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);
})
</div> </div>
1
қосылды

Мен уәделермен асинхронды тапсырмалар тізбегіндегі циклдар жасауға көмектесетін модульді жаздым, ол жоғарыда келтірілген жауапқа негізделген шуандопазо

/**
 * 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

1
қосылды
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())
1
қосылды
Pls тек жауаптар кодын жібермейді
қосылды автор danopz, көзі

ES6 уәдесін қолдану арқылы мен осымен келдім. Ол уәделерді тізбейді және уәде береді. Бұл техникалық уақытша цикл емес, бірақ уәделерді синхронды түрде қалай өзгерту керектігін көрсетеді.

<div class="snippet" data-lang="js" data-hide="false"> <div class="snippet-code">

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

Міне менің скриптің.

0
қосылды
Жақсы. Рахмет, @Bergi. Мен өз мысалдарымды жаңарттым.
қосылды автор mqsoh, көзі
Кеңес: new Promise конструкторының орнына Promise.resolve (true) пайдаланыңыз
қосылды автор Bergi, көзі

ES6 уәделерін қолданып, шляманы сақинаға лақтырамын деп ойладым.

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

орындаушы аргументі бірдей Promise конструкторына жіберілгендіктен, бірақ ол табысты кері шақыртылғанға дейін бірнеше рет шақырылады. before_retry функциясы сәтсіз әрекеттерде реттелетін қателерді өңдеуге мүмкіндік береді. Егер шын мәнінде құндылықты қайтарса, онда ол табыстың нысаны ретінде қарастырылады және «цикл» аяқталады, нәтижесінде осы ақиқат. Егер before_retry функциясы тіркелмеген болса немесе фальси мәнін қайтарса, онда цикл басқа иерархия үшін іске қосылады. Үшінші нұсқа: before_retry функциясы қатені өзі жібереді. Егер бұл орын алса, онда бұл қатені қате деп өтіп, «цикл» аяқталады.


Міне мысал:

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

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

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

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

0
қосылды