2016-09-27 22 views
17

Tôi có thể giải quyết một cách không đồng bộ một loạt các lời hứa với Promise.all(array). Tuy nhiên .then() sẽ chỉ chạy sau khi tất cả các lời hứa đó đã được giải quyết. Làm thế nào tôi có thể thực hiện các hành động như lời hứa được giải quyết?Thực hiện các hành động như lời hứa được hoàn thành bằng cách sử dụng Promise.all()

Ví dụ: tôi muốn tải tất cả các đoạn văn từ một bài viết không đồng bộ bằng cách sử dụng Promise.all() - Bằng cách này, mạng yêu cầu tất cả các đám cháy cùng một lúc. Nếu đoạn 1 được thực hiện tải, tôi muốn nó để render cho trang - nhưng chỉ khi nó được thực hiện tải trước đoạn 2, sau đó tôi muốn đoạn 2 để tải. Nếu đoạn 3 được thực hiện tải và 2 không phải là, tôi muốn 3 để chờ 2 trước khi kết xuất trang. Và cứ thế.

tôi đã cố gắng một cái gì đó như thế này nhưng tôi không biết phải làm gì tiếp theo:

var getStuff = function(number, time){ 
    return new Promise(function(resolve, reject){ 
    window.setTimeout(function(){resolve(`${number} - Done.`)}, time); 
    }); 
}; 

Promise.all([ getStuff(1, 200), 
       getStuff(2, 100), 
       getStuff(3, 250), 
       getStuff(4, 200), 
       getStuff(5, 300), 
       getStuff(6, 250), 
       getStuff(7, 5000)]) 
.then(function(data){ 
    console.log(data); 
}); 

Làm thế nào tôi có thể nhận được các bản ghi giao diện điều khiển các dữ liệu xảy ra cái khác - mà không giải quyết từng hứa với một then() trước khi đưa ra yêu cầu tiếp theo? Có cách nào tốt hơn để làm điều này?

+0

Một số thư viện hứa hẹn có * tiến trình * callbacks. – alex

+0

Không có cách nào để có được hành vi đó bằng cách sử dụng es6 bản địa? Không thêm thư viện khác vào dự án của tôi? – hackrnaut

+1

Tại sao không chỉ 'getStuff (...)' và 'sau đó' render, cho mỗi lời hứa? –

Trả lời

18

Bạn không thể đạt được đơn đặt hàng này bằng cách sử dụng Promise.all vì nó chờ mọi thứ giải quyết trước khi thực hiện bất kỳ điều gì. Tạo những lời hứa cá nhân và ngọn lửa của yêu cầu của họ song song:

// create promises and make parallel (concurrent) requests 
const s1 = getStuff(1, 200); 
const s2 = getStuff(2, 100); 
const s3 = getStuff(3, 250); 
// ... 

Sau đó, tạo ra một chuỗi phản ứng về cách xử lý chúng (stuff1 trước stuff2, stuff2 trước stuff3 vv)

// create a chain of reaction order to the results of parallel promises 
s1 
    .then(console.log) // s1 resolved: log result 
    .then(() => s2) // chain s2 
    .then(console.log) // s2 resolved: log result 
    .then(() => s3) // chain s3 
    // ... 
    .then(() => {  // chain another function at at the end for when all promises resolved 
    // all promises resolved (all data was logged) 
    } 

Để phản ứng với kết quả hứa hẹn theo cùng thứ tự các lời hứa đã được tạo, bạn có thể thay đổi chức năng getStuff để tự động chuỗi phản ứng bằng cách sử dụng Array.prototype.reduce:

var times = [200, 100, 250, 200, 300, 250, 5000]; 
 

 
var getStuff = function(time, index) { // swap the order of arguments so number is the index passed in from Array.map 
 
    return new Promise((resolve, reject) => { 
 
    window.setTimeout(() => { 
 
     resolve(`${index + 1} - Done.`); // use index + 1 because indexes start at 0 
 
    }, time); 
 
    }); 
 
}; 
 

 
times 
 
    // map each time to a promise (and number to the index of that time + 1) and fire of a request 
 
    .map(getStuff) 
 
    // dynamically build a reaction chain for the results of promises 
 
    .reduce((chain, promise) => { 
 
    return chain 
 
     .then(() => promise) 
 
     .then(console.log); 
 
    }, Promise.resolve()) 
 
    .then(() => { 
 
    // all promises resolved (all data was logged in order) 
 
    });

+1

Ahh, điều này có ý nghĩa! Về cơ bản, ngay sau khi tôi gọi getStuff, họ sẽ thực hiện các yêu cầu mạng và nếu thực hiện các chuỗi sau, tôi sẽ nhận được hành vi tôi muốn bởi vì mỗi lời hứa sẽ được giải quyết trước khi. – hackrnaut

+2

Ngay sau khi bạn tạo một Lời hứa, nó sẽ chạy yêu cầu của bạn. Bạn có thể xây dựng chuỗi tại bất kỳ thời điểm nào sau đó, bất kể lời hứa có được giải quyết hay không vào thời điểm đó. Nếu lời hứa được giải quyết trước khi bạn thực hiện chuỗi nó sẽ chỉ giữ giá trị của nó cho đến khi bạn thực hiện chuỗi. Nếu bạn thực hiện chuỗi đầu tiên, chuỗi sẽ đợi lời hứa nhận giá trị. Đó là vẻ đẹp của lời hứa. Chúng luôn phân giải không đồng bộ và bạn luôn có thể giả vờ như thể bạn đã có các giá trị khi mã hóa chúng. – nem035

2

Tôi biết nó không phải là mẹ đẻ, nhưng với bluebird bạn có thể sử dụng Promise.some (để fullfill sau count lời hứa đã được thực hiện) hoặc Promise.mapSeries (để fullfill lời hứa trong series) để bằng cách nào đó đạt được dòng chảy bạn mong đợi. câu trả lời

Bluebird API

6

nem035 là tại chỗ trên. Tôi muốn chỉ ra rằng trong trường hợp này, bạn thường muốn thực hiện cùng một hành động khi có yêu cầu và một hành động khác khi chúng được thực hiện xong.

Bạn có thể sử dụng .all cho rằng với .map:

Promise.all([ getStuff(1, 200), 
      getStuff(2, 100), 
      getStuff(3, 250), 
      getStuff(4, 200), 
      getStuff(5, 300), 
      getStuff(6, 250), 
      getStuff(7, 5000)] 
.map(request => request.then(v => { 
    console.log("Request done! Got," v); // or some action per request 
    return v; 
})).then(data => console.log(data)); 

Bạn có thể ông chủ này hơn nữa với .map bằng cách sử dụng thực tế bạn đang sử dụng các chức năng tương tự cho mọi yêu cầu:

Promise.all([[1, 200], 
      [2, 100], 
      [3, 250], 
      [4, 200], 
      [5, 300], 
      [6, 250], 
      [7, 5000]]) 
.map((a, b) => getStuff(a, b)) 
.map(request => request.then(v => { 
    console.log("Request done! Got," v); // or some action per request 
    return v; 
})).then(data => console.log(data)); 

Và thêm:

Promise.all([200, 100, 250, 200, 300, 250, 5000]) 
.map((a, i) => getStuff(a, i + 1)) 
.map(request => request.then(v => { 
    console.log("Request done! Got," v); // or some action per request 
    return v; 
})).then(data => console.log(data)); 

Hoặc với bluebird:

const sideEffect = v => console.log("Got partial result", v)); 
const data = [200, 100, 250, 200, 300, 250, 5000]; 
Promise.map(data, (a, i) => getStuff(a, i + 1).tap(sideEffect)) 
     .then(data => console.log(data)); 

Tất nhiên - bạn chỉ cần sửa chữa phụ trợ của bạn, đó là hoàn toàn bất hợp lý để yêu cầu khách hàng để thực hiện 7 yêu cầu cho các bộ phận khác nhau của dữ liệu - có backend mất dãy.

0

Hoạt động bình thường: Bạn có thể sử dụng Promise.all() một cách an toàn. Những người thực hiện lời hứa sẽ bị sa thải song song và kết quả sẽ được trả về theo thứ tự bạn chèn lời hứa của mình vào mảng hứa hẹn. Sau đó, tùy thuộc vào bạn để sắp xếp chúng theo cách bạn thích. Chẳng hạn như trong đoạn mã sau, chúng tôi có năm lời hứa sẽ giải quyết ngẫu nhiên trong vòng 5 giây. Bất kể thời gian giải quyết của họ, bạn sẽ nhận được kết quả khi giải quyết mới nhất;

var promises = [ new Promise(v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000))), 
 
       ]; 
 
Promise.all(promises) 
 
     .then(result => console.log(result.reduce((p,c) => p + "\n" + c)));

gì bạn muốn: Nhưng sau đó bạn không muốn đợi chừng nào một kết thúc cuối cùng nhưng thay vào đó muốn xử lý chúng theo thứ tự, càng sớm càng tốt họ nhận được giải quyết. Sau đó, Array.prototype.reduce() là người bạn tốt nhất của bạn ở đây. Chẳng hạn như

var promises = [ new Promise(v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000))) 
 
       ]; 
 
promises.reduce((p,c) => p.then(result => (console.log(result + "\n"),c))) 
 
     .then(result => (console.log(result + "\n")));

hãy chạy mã nhiều lần để xem cách mã ứng xử. Văn bản sẽ được cập nhật càng sớm càng tốt thì lời hứa sẽ được giải quyết nhưng chỉ khi đến lượt nó. Vì vậy, nếu 1st giải quyết sau 2, chúng tôi sẽ thấy 1 và 2 xuất hiện cùng một lúc theo thứ tự của họ, nhưng họ sẽ không chờ cho đến ngày thứ 3 để giải quyết ...

Các vấn đề liên quan