2017-11-09 43 views
13

Chcę wykonać synchroniczne działanie funkcji przy użyciu promise. Mam pętlę, która przekazuje dane do wstawienia do funkcji insert i po wstawieniu jednego wiersza chcę sprawdzić nr. wierszy istnieje w tabeli, dlatego wykonuję operację select.sekwencyjne wywołanie za pomocą obietnicy wewnątrz pętli

Ale problem polega na tym, że jeśli są 3 rekordy, wstawi wszystkie 3 rekordy, a następnie moja funkcja wyboru zostanie wykonana. to, czego chcę, to po wywołaniu wstawienia jednej funkcji wyboru rekordu.

tutaj jest mój pseudo kod jak cały kod obejmuje wiele operacji

for(var i=0; data.length ; i++){ 

self.executeFeedbackTrack(data); 

} 


executeFeedbackTrack:function(callInfo){ 
    var self=this; 
    return self.insertFeedbackTrack(callInfo).then(function(data){ 

       console.log("insertFeedbackTrack status "+status); 

     return self.getFeedbackTrack(); 

    }); 
}, 

getFeedbackTrack :function(){ 

    return new Promise(function(resolve,reject){ 
     var objDBFeedbackTrack = new DBFeedbackTrack(); 
     objDBFeedbackTrack.selectFeedbackTrack(function(arrayCallRegisters){ 
      if(arrayCallRegisters){ 

      console.log("notification.js no. of feedbacks "+arrayCallRegisters.length); 

      resolve(arrayCallRegisters.length); 

      } 

     }); 
    }); 


}, 
insertFeedbackTrack :function(callInfo){ 

return new Promise(function(resolve,reject){ 
    var objDBFeedbackTrack = new DBFeedbackTrack(); 
    objDBFeedbackTrack.insertFeedbackTrack(callInfo.callNumber,callInfo.callServiceType,function(status){ 

      resolve(status); 
      $('#loader').hide(); 

    }); 
}); 

} 
+0

Proszę zobaczyć dostarczoną odpowiedź. –

Odpowiedz

3

Można to rozwiązać za pomocą bardzo przydatnej biblioteki JS Ramda. Koncepcja polega na użyciu dwóch metod, jedna to R.partial, a druga to R.pipeP.

Najpierw utwórz tablicę obietnic z tablicy data, jak na przykład.

var promises = data.map(function(i) { 
    return R.partial(sample, [i]) 
}); 

Następnie można przekazać tę obietnicę R.pipeP, tak że może być wykonywane jeden po drugim. jak poniżej.

var doOperation = R.pipeP.apply(this, promises) 

Proszę wykonać poniższy załączony załącznik.

// Sample promise returning function 
 
function sample(d) { 
 
    return new Promise(function(resolve, reject){ 
 
    setTimeout(function() { 
 
     console.log('resolved for:' + d); 
 
     resolve(d); 
 
    }, 1000) 
 
    }) 
 
} 
 

 
// Sample data 
 
var data = [1, 2, 3, 4, 5] 
 

 
// Converting data array to promise array 
 
var promises = data.map(function(i) { 
 
    return R.partial(sample, [i]) 
 
}); 
 

 
var doOperation = R.pipeP.apply(this, promises) 
 
doOperation();
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Więc w twoim przypadku, kod będzie wyglądał ten

var promises = data.map(function(i) { 
    return R.partial(self.executeFeedbackTrack, [i]) 
}); 
var doOperation = R.pipeP.apply(this, promises) 
doOperation(); 
4

Wygląda mi na to, jest to spowodowane przez wykonanie serii asynchronicznych wkładek, i zakładając, że get z insert n (wewnątrz .then()) jest wywoływana przed wykonaniem insert n+1. Jednak nie jestem świadomy żadnej takiej gwarancji, w JavaScript; wszystko, co wiem, to to, że then n zostanie wywołany po insert n, a nie że zostanie wywołany przed insert n+1.

Proponuję unikanie tej kombinacji tradycyjnego i opartego na oddzwanianiu kodu, a zamiast tego wstawię etap iteracji do getFeedbackTrack().then. Zakładając, że to zrozumienie problemu jest poprawna, to coś jak następujące powinny działać:

function iterate(i) { 
    if (i < data.length) { 
     obj.insertFeedbackTrack(data[i]).then(function(insertResult) { 
      self.getFeedbackTrack().then(function(getResult) { 
       // this line is the important one, replacing the `for` loop earlier 
       iterate(i+1); 
      }); 
     }); 
    } 
} 

iterate(0); 

Robiąc to, by zagwarantować, że insert do następnego elementu nie występuje dopóki prąd select wykonuje pomyślnie.

Oczywiście, możesz również zmienić to ustawienie, aby użyć połączonego .then zamiast zagnieżdżonego; Użyłem zagnieżdżonego zamiast łańcuchowego, aby podkreślić kolejność wywołań zwrotnych.

+0

próbujesz zastąpić pętlę za pomocą funkcji rekursywnej? – Hunt

+0

Tak, dokładnie tak - zamień pętlę for na łańcuch rekursywny zwrotów 'then', aby wymusić określoną kolejność zdarzeń/wywołań zwrotnych. –

+0

cóż, muszę wprowadzić wiele zmian tylko po to, aby zmienić pętlę for, czy istnieje sposób obejścia tego problemu? – Hunt

6

Poprzednia odpowiedź jest dobra, ale jeśli używasz nodejs lub babel lub używasz tylko nowoczesnych przeglądarek. Możesz użyć pary async-await, to jest es8 stuff.

let insertFeedbackTrack = function(){ return new Promise(/***/)}; 
let getFeedbackTrack = function(){ return new Promise(/***/)}; 
let processResult = async function(data){ 
    let feedbacks = []; 
    for(let i=0;i<data.length;i++){ 
     let insertedResult = await insertFeedbackTrack(data[i]);//perhaps you will return an id; 
     let feedbackTrack = await getFeedbackTrack(insertedResult.id); 
     feedbacks.push(feedbackTrack); 
    } 
    return feedbacks; 
} 

processResult(data).then(/** do stuff */) 
0

Oto jak bym kolejno obietnic połączeń w pętli (używam ES7). Najpierw zdefiniować podstawowe dane:

const data = [0,1,2,3]; 

Następnie niech symulować jakiś proces długo pracuje, więc stwórzmy funkcję zwracającą obietnicę (można myśleć o tym, jak symulowanym zamówienie sieci, czy cokolwiek pasuje do Twojego potrzeby)

const promiseExample = (item) => 
    new Promise((res) => { 
    setTimeout(() => { 
     console.log('resolved ', item); 
    res(item); 
    }, 1000); 
}); 

Teraz stwórzmy szereg obietnic. To, co robi następna linia kodu: dla każdego elementu w danych tablicy, zwróć fabrykę obiecującą. Fabryka obietnicy to funkcja, która owija pewną obietnicę, nie uruchamiając jej.

const funcs = data.map(item => async() => await promiseExample(item)); 

Teraz zaczyna się tutaj aktualny kod. Potrzebujemy funkcji, która wykonuje rzeczywistą serializację. Ponieważ musi obsługiwać szereg obiecującychFactories, dzielę go na dwie funkcje, jedną na serializację pojedynczej obietnicy i jedną na obsługę szeregu obiecującychFactories.

const serializePromise = promiseFactoryList => 
    promiseFactoryList.reduce(serialize, Promise.resolve([])); 

const serialize = async (promise, promiseFactory) => { 
    const promiseResult = await promise; 
    const res = await promiseFactory(); 
    return [...promiseResult, res]; 
}; 

Teraz można po prostu nazwać tak:

serializePromise(funcs).then(res => { 
    console.log('res', res); 
}); 

Jak widać, kod jest dość prosty, elegancki, funkcjonalny i nie wymaga żadnego zewnętrznego uzależnienia. Mam nadzieję, że to odpowie na twoje pytanie i pomoże!

const serializePromise = promiseFactoryList => 
 
    promiseFactoryList.reduce(serialize, Promise.resolve([])); 
 

 
const serialize = async (promise, promiseFactory) => { 
 
    const promiseResult = await promise; 
 
    const res = await promiseFactory(); 
 
    return [...promiseResult, res]; 
 
}; 
 

 
const data = [0,1,2,3]; 
 

 
const promiseExample = (item) => 
 
    new Promise((res) => { 
 
\t setTimeout(() => { 
 
\t console.log('resolved ', item); 
 
\t res(item); 
 
    }, 1000); 
 
    }); 
 

 
const funcs = data.map(item => async() => await promiseExample(item)) 
 

 
serializePromise(funcs).then(res => { 
 
    console.log('res', res); 
 
});

+0

@ Hunt, sprawdź to :) –

1

użyć wydajnością w takich przypadkach, jeżeli za pomocą funkcji generatora.

for(var i = 0; i < order.tasks.length; i++){ 
     if(order.tasks[i].customer_id === 0){ 
      var name = order.tasks[i].customer_name.split(" ") 
      const customers = yield db.queryAsync(
      `INSERT INTO customers(
       business_id) 
      VALUES(?) 
      `,[order.business_id]) 
     } 
     } 

Albo używam funkcji samokierowania w przypadku połączeń zwrotnych.

var i = 0; 
(function loop() { 
    if (i < data.length) { 
     task_ids1.push([order.tasks[i].task_id]) 
     i++; 
     loop(); 
    } 
}());