一、問題描述
有很多個請求需要依次發送,待上一個請求完成之后再發送下一個請求,發生異常時也要能夠繼續后面的請求。
二、思路
一個請求完成之后再發送下一個請求,關鍵在於發送一個之后先停下來等待該請求完成,處理之后再繼續下一個請求。生成器generator里面的yield語句可以分割代碼,程序遇到yield會停住,通過next語句可以一次執行一個yield分割的語句,本文嘗試使用生成器完成依次發送多個請求的功能。
生成器示例如下:
1 // 生成器通過函數名前加*創建 2 function * generatorFn() { 3 yield 1; 4 yield 2; 5 } 6 // 調用生成器 7 let g = generatorFn(); 8 // 通過next方法執行,返回yield后面的值,且整個生成器未執行完之前done為false 9 console.info(g.next()); // {value: 1, done: false} 10 console.info(g.next()); // {value: 2, done: false} 11 // 執行完之后value為undefined,done為true 12 console.info(g.next()); // {value: undefined, done: true} 13 console.info(g.next()); // {value: undefined, done: true}
三、步驟
通過依次發送100個不同的請求來介紹,首先先把100個請求放入生成器內:
1 let baseUrl = 'https://jsonplaceholder.typicode.com/todos/'; 2 function* generator() { 3 for (let i = 1; i <= 100; i++) { 4 yield fetch(baseUrl + i); 5 } 6 }
接下來要先調用next()開始一個yield語句,讓請求發送出去,然后處理好結果后再次調用next(),如此循環,知道next()返回done: true,通過遞歸實現如下:
1 function run(res) { 2 if (!res.done) { 3 res.value 4 .then((response) => response.json()) 5 .then((json) => { 6 console.log(json); 7 document.writeln('id: ' + json.id + '<br>'); 8 run(gen.next()); 9 }); 10 } 11 } 12 13 // 開始執行 14 let gen = generator(); 15 run(gen.next());

完整代碼如下:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <title>fetch one by one</title> 7 <script> 8 // 構造100個請求 9 let baseUrl = 'https://jsonplaceholder.typicode.com/todos/'; 10 function* generator() { 11 for (let i = 1; i <= 100; i++) { 12 yield fetch(baseUrl + i); 13 if (i == 4) { // 故意制造一個異常 14 yield fetch('fsfdsfsdf'); 15 } else { 16 yield fetch(baseUrl + i); 17 } 18 } 19 } 20 21 22 // 處理請求結果並發送下一次請求 23 function run(res) { 24 if (!res.done) { 25 res.value 26 .then((response) => response.json()) 27 .then((json) => { 28 console.log(json); 29 document.writeln('id: ' + json.id + '<br>'); 30 run(gen.next()); 31 }) 32 .catch((err) => { // 處理一下異常 33 console.info(err); 34 run(gen.next()); 35 }); 36 } 37 } 38 // 開始執行 39 let gen = generator(); 40 run(gen.next()); 41 </script> 42 </head> 43 <body></body> 44 </html>
或者封裝一下fetch,在請求成功之后調用生成器的next,執行下一個fetch,省去了調度的run函數:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body></body> <script> let baseUrl = 'https://jsonplaceholder.typicode.com/todos/'; function* generator() { for (let i = 1; i <= 100; i++) { yield ajax(baseUrl + i); } } function ajax(url) { fetch(url) .then((res) => res.json()) .then((res) => { console.info(res.id); gen.next(); }); } let gen = generator(); gen.next(); </script> </html>
四、其他實現
1.通過promise的then實現。先得到一個成功的promsie,每次循環在promise.then里面返回一個新的promise,在新promise內發送請求,待請求成功后再resolve新promise,這樣下一次then的時候一定是上次resolve之后
function ajaxQuene() { let baseUrl = 'https://jsonplaceholder.typicode.com/photos/'; let promise = Promise.resolve(); for (let i = 1; i <= 100; i++) {
// 關鍵就是要接收新返回的promise promise = promise.then(() => { return new Promise((resolve, reject) => { fetch(baseUrl + i).then(res=>res.json()).then((res)=>{ console.info(res.id); resolve(); }) }) }) } } ajaxQuene();
下面的分步驟代碼會比較直接:
let promise = Promise.resolve(); promise = promise.then((v) => { return new Promise((resolve, reject) => { fetch('https://jsonplaceholder.typicode.com/photos/1').then((res) => { console.info(1); resolve(res); }); }); });
// 這個then的執行必定是上個返回的promise在請求完成調用resolve之后 promise = promise.then((v) => { return new Promise((resolve, reject) => { fetch('https://jsonplaceholder.typicode.com/photos/2').then((res) => { console.info(2); resolve(res); }); }); }); promise = promise.then((v) => { return new Promise((resolve, reject) => { fetch('https://jsonplaceholder.typicode.com/photos/3').then((res) => { console.info(3); resolve(res); }); }); });
2.await會強制其他代碼等待,直到后面的promise執行完畢,可以使用async和await完成類似功能:
1 // 使用async和await 2 let baseUrl = 'https://jsonplaceholder.typicode.com/todos/'; 3 async function ajax() { 4 for (let i = 1; i <= 100; i++) { 5 await fetch(baseUrl + i).then((response) => response.json()) 6 .then((json) => { 7 console.log(json); 8 document.writeln('id: ' + json.id + '<br>'); 9 }) 10 .catch((err) => { // 處理一下異常 11 console.info(err); 12 }); 13 } 14 } 15 ajax();
以上項目中需要依次發送異步請求的實現方法,JS異步的發展就是讓異步處理起來越來越像同步。