2015-01-25 25 views
6

Tôi đang cố gắng tạo vòng lặp không đồng bộ với kiểu gốc ES6 promisesloại hoạt động nhưng không chính xác. Tôi cho rằng tôi đã phạm sai lầm lớn ở đâu đó và tôi cần một ai đó cho tôi biết nó ở đâu và làm thế nào nó được thực hiện một cách chính xácVòng lặp với lời hứa gốc;

var i = 0; 

//creates sample resolver 
function payloadGenerator(){ 
    return function(resolve) { 
     setTimeout(function(){ 
      i++; 
      resolve(); 
     }, 300) 
    } 
} 

// creates resolver that fulfills the promise if condition is false, otherwise rejects the promise. 
// Used only for routing purpose 
function controller(condition){ 
    return function(resolve, reject) { 
     console.log('i =', i); 
     condition ? reject('fin') : resolve(); 
    } 
} 

// creates resolver that ties payload and controller together 
// When controller rejects its promise, main fulfills its thus exiting the loop 
function main(){ 
    return function(resolve, reject) { 
     return new Promise(payloadGenerator()) 
      .then(function(){ 
       return new Promise(controller(i>6)) 
      }) 
      .then(main(),function (err) { 
       console.log(err); 
       resolve(err) 
      }) 
      .catch(function (err) { 
       console.log(err , 'caught'); 
       resolve(err) 
      }) 
    } 
} 


new Promise(main()) 
    .catch(function(err){ 
     console.log('caught', err); 
    }) 
    .then(function(){ 
     console.log('exit'); 
     process.exit() 
    }); 

Bây giờ kết quả:

/usr/local/bin/iojs test.js 
i = 1 
i = 2 
i = 3 
i = 4 
i = 5 
i = 6 
i = 7 
fin 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
error: [TypeError: undefined is not a function] 
caught [TypeError: undefined is not a function] 
exit 

Process finished with exit code 0 

Phần tốt: đi đến cuối đường.

Phần xấu: nó bắt được một số lỗi và tôi không biết tại sao.

+1

Bất kể thư viện nào được sử dụng theo cách bạn đang sử dụng lời hứa là _really_ lạ. Mục tiêu cuối cùng của bạn ở đây là gì? Bạn muốn thực hiện một "trong khi" với lời hứa? –

+1

'.then (main(), function (err) {'. Khi nào 'main()' đang làm ở đó? –

+0

Bạn quên nói cho chúng ta biết mã này được cho là làm gì. – JLRishe

Trả lời

7

Bất kỳ chức năng trợ giúp nào với vòng lặp lời hứa Tôi đã thấy thực sự làm cho nó tồi tệ hơn nhiều so với những gì bạn có thể làm trong hộp với đệ quy.

Nó là một chút đẹp hơn với .thenReturn nhưng yeah:

function readFile(index) { 
    return new Promise(function(resolve) { 
     setTimeout(function() { 
      console.log("Read file number " + (index +1)); 
      resolve(); 
     }, 500); 
    }); 
} 

// The loop initialization 
Promise.resolve(0).then(function loop(i) { 
    // The loop check 
    if (i < len) {    // The post iteration increment 
     return readFile(i).thenReturn(i + 1).then(loop); 
    } 
}).then(function() { 
    console.log("done"); 
}).catch(function(e) { 
    console.log("error", e); 
}); 

Xem nó trong jsfiddle http://jsfiddle.net/fd1wc1ra/

này là khá nhiều chính xác tương đương với:

try { 
    for (var i = 0; i < len; ++i) { 
     readFile(i); 
    } 
    console.log("done"); 
} catch (e) { 
    console.log("error", e); 
} 

Nếu bạn muốn làm vòng lặp lồng nhau nó là chính xác như nhau:

http://jsfiddle.net/fd1wc1ra/1/

function printItem(item) { 
    return new Promise(function(resolve) { 
     setTimeout(function() { 
      console.log("Item " + item); 
      resolve(); 
     }, 500); 
    }); 
} 

var mdArray = [[1,2], [3,4], [5,6]]; 
Promise.resolve(0).then(function loop(i) { 
    if (i < mdArray.length) { 
     var array = mdArray[i]; 
     return Promise.resolve(0).then(function innerLoop(j) { 
      if (j < array.length) { 
       var item = array[j]; 
       return printItem(item).thenReturn(j + 1).then(innerLoop); 
      } 
     }).thenReturn(i + 1).then(loop); 
    } 
}).then(function() { 
    console.log("done"); 
}).catch(function(e) { 
    console.log("error", e); 
}); 
+0

Thật tuyệt vời, cảm ơn bạn! mã của bạn ở đây là khác nhau từ mã trên jsfiddle, tôi đã nhầm lẫn lúc đầu tiên.Jsfiddle phiên bản làm việc cho tôi kể từ khi triển khai lời hứa của iojs không có 'thenReturn' – amdc

+0

@amdc' thenReturn' là từ bluebird nhưng có thể được thực hiện trên hứa hẹn bản địa – Esailija

+0

Hoạt động rất tốt! Tôi muốn có thể làm như 5 vòng cùng lúc và khi một trong số đó được thực hiện, hãy tiếp tục với thứ 6 và tiếp tục cho đến khi mảng được thực hiện. Thanh toán http: //stackoverflow.com/q/36664272/1760313 – Tom

0

Hãy thử đăng nhập err.stack thay vì chỉ err khi bắt lỗi lỗi.

Trong trường hợp này, có vẻ như resolvereject không được xác định trong hàm ẩn danh được trả về từ main sau khi quá trình lặp lại ban đầu hoàn tất. Tôi không thể hoàn toàn theo dõi luồng điều khiển của bạn, nhưng điều đó có vẻ hợp lý - sau 7 lần lặp lại hoàn tất, sẽ không còn bất kỳ lời hứa mới nào nữa. Tuy nhiên, có vẻ như mã vẫn đang cố gắng chạy như có nhiều lời hứa hơn để giải quyết.

Chỉnh sửa: Đây là sự cố .then(main(),function (err) {. Gọi tự động main sẽ gây ra resolvereject bên trong chức năng ẩn danh sẽ không được xác định. Theo cách tôi đọc, chỉ có thể gọi main làm đối số cho hàm tạo Promise.

2

Nếu tất cả các bạn đang cố gắng làm là đếm đến 7 với lời hứa, thì điều này sẽ làm điều đó:

function f(p, i) { 
    return p.then(function() { 
    return new Promise(function(r) { return setTimeout(r, 300); }); 
    }) 
    .then(function() { console.log(i); }); 
} 

var p = Promise.resolve(); 
for (var i = 0; i < 8; i++) { 
    p = f(p, i); 
} 
p.then(function() { console.log('fin'); }) 
.catch(function(e) { console.log(e.message); }); 

Looping với lời hứa là khó khăn, bởi vì nó hầu như không thể không rơi vào JavaScript closures in a loop trap, nhưng có thể thực hiện được. Các công trình trên vì nó đẩy tất cả việc sử dụng .then() vào một hàm con f của vòng lặp (tức là tránh xa vòng lặp).

Một giải pháp an toàn hơn, mà tôi sử dụng, là từ bỏ vòng hoàn toàn và tìm ra các mẫu như forEachreduce bất cứ khi nào tôi có thể, bởi vì họ có hiệu quả buộc các phụ chức năng trên bạn:

[0,1,2,3,4,5,6,7].reduce(f, Promise.resolve()) 
.then(function() { console.log('fin'); }) 
.catch(function(e) { console.log(e.message); }); 

đây f được cùng chức năng như trên. Try it.

Cập nhật:In ES6 bạn cũng có thể sử dụng for (let i = 0; i < 8; i++) để tránh sự "đóng cửa trong một vòng lặp" bẫy mà không đẩy mã vào một phụ chức năng f.

PS: Sai lầm trong ví dụ của bạn là .then(main(), - nó cần phải được .then(function() { return new Promise(main()); }, nhưng thực sự, tôi nghĩ rằng bạn đang sử dụng mô hình sai. main() nên trả lại một lời hứa, không được bao bọc bởi một.

+0

Đây là vòng lặp for - tôi nghĩ OP sẽ thực hiện một 'while' sẽ không hoạt động với chuỗi có thể có được vì bạn sẽ không biết bạn cần bao nhiêu chuỗi trước. –

+0

Có thể OP có nghĩa là điều kiện phụ thuộc vào các bước trước đó đã được thực thi? Nếu vậy, thì có sự đệ quy sẽ là cần thiết. Trong mọi trường hợp, sử dụng một vòng lặp while thay vì for-loop, không giải quyết được điều này, hoặc có nghĩa là.Tôi đã thay đổi vòng lặp for thành vòng lặp while để hiển thị điều đó. OP cho biết trong một bình luận anh đang cố gắng đếm đến 7 lời hứa, và yêu cầu ai đó chỉ ra sai lầm của anh, điều mà tôi hy vọng là tôi đã làm. – jib

+0

Tất nhiên việc thay đổi vòng lặp for thành một cấu trúc vòng lặp đồng bộ khác sẽ không giúp ích gì - tôi có nghĩa là khái niệm số lần lặp lại phụ thuộc vào trạng thái trước đó. –

0

Tôi cũng đã xem xét các giải pháp khác nhau và không thể tìm thấy một giải pháp thỏa mãn tôi, vì vậy tôi đã tạo xong sản phẩm của riêng mình. Đây là trường hợp nó hữu ích cho người khác:

Ý tưởng là tạo ra một loạt các trình tạo lời hứa và cung cấp mảng này cho một hàm trợ giúp sẽ thực thi lời hứa này đến cái khác.

Trong trường hợp của tôi chức năng helper chỉ đơn giản là thế này:

function promiseChain(chain) { 
    let output = new Promise((resolve, reject) => { resolve(); }); 
    for (let i = 0; i < chain.length; i++) { 
     let f = chain[i]; 
     output = output.then(f); 
    } 
    return output; 
} 

Sau đó, ví dụ, để tải nhiều URL cái khác, mã sẽ là như thế này:

// First build the array of promise generators: 

let urls = [......]; 
let chain = []; 
for (let i = 0; i < urls.length; i++) { 
    chain.push(() => { 
     return fetch(urls[i]); 
    }); 
} 

// Then execute the promises one after another: 

promiseChain(chain).then(() => { 
    console.info('All done'); 
}); 

Ưu điểm của phương pháp này là nó tạo ra mã tương đối gần với vòng lặp thông thường, và với thụt đầu dòng tối thiểu.

0

Tôi đã có nhu cầu tương tự và đã thử câu trả lời được chấp nhận, nhưng tôi đã gặp sự cố với thứ tự hoạt động. Promise.all là giải pháp.

function work(context) { 
    return new Promise((resolve, reject) => { 
    operation(context) 
     .then(result => resolve(result) 
     .catch(err => reject(err)); 
    }); 
} 

Promise 
    .all(arrayOfContext.map(context => work(context))) 
    .then(results => console.log(results)) 
    .catch(err => console.error(err)); 
Các vấn đề liên quan