需求分析
當我們想依次執行多個異步請求時,或者實作setTimeout為setInterval時,可以使用Promise對象和async await關鍵字來實作該功能。
尤其是,我們在使用node做網絡爬蟲時,需要進行大量的依次進行的異步請求。
波及到的知識點如下:
1、數組的reduce方法
reduce()
方法對數組中的每個元素執行一次處理函數,将其結果彙總為單個傳回值。
reduce詳情:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
2、Promise
微任務,立即執行的函數,resolve和reject為完成狀态下執行的方法。
3、async和await
async函數執行結果為一個Promise對象;await 等待一個Promise執行完成後的結果,即resolve或reject的值。
實作代碼:
注意:reduce方法中,有兩種實作方式。
reduce必須接收一個Promise對象,且每次執行reduce處理函數,都是一個Promise對象,是以reduce的處理函數傳回值必須為Promise
setTime函數是一個異步方法,是以必須要用async await來等待setTime異步函數的執行完成。
是以我們的寫法如下:
var setTime = function(v){
return new Promise(function(resolve, reject){
setTimeout(()=> {
console.log(v)
resolve();
}, 1000)
})
}
var arr = [10,20,30,40];
arr.reduce(async function(acc,cur,index){
return acc.then(() => {
//第二種實作方法
// return new Promise(async (resolve, reject) => {
// await setTime(cur);
// resolve();
// })
return (async function a(){
//await之前的宏任務,因為上一個異步的await完成後,就會立即執行該宏任務
//console.log(cur)
await setTime(cur);
//await之後的宏任務,該await執行完後,立即執行後面的宏任務,相當于對該await的補充
// console.log(cur)
})()
})
},Promise.resolve())
深入了解:
核心思想是:宏任務執行建一個Promise鍊,類似下面這種:——Promise鍊是宏任務瞬間執行完成的,結合數組reduce累加器可以累加構造Promise鍊
Promise.resolve().then(異步處理函數1).then(異步處理函數2).then....
是以我們的代碼目的是建Promise鍊,比如下面這種寫法:
var setTime = function (v) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log(v)
resolve();
}, 1000)
})
}
var arr = [10, 20, 30, 40];
arr.reduce(async function (acc, cur, index) {
return acc.then(() => {
return setTime(cur);
})
}, Promise.resolve())
我們的目的是構造Promise鍊,如下也是可以的:
var setTime = function (v) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log(v)
resolve();
}, 1000)
})
}
var p = Promise.resolve();
for(let i = 0; i < 5; i++){
p = p.then(function(){
return setTime(i);
})
}
你是怎麼想的呢?
封裝一個Promise鍊函數
注意:for循環内的宏任務瞬間執行完,執行次數為arr.length - 1,之後是promise鍊的鍊式執行;
let promiseChain = function (arr) {
let setTime = (v => {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log(v)
resolve();
}, 1000)
})
})
let p = Promise.resolve();
for (let i = 0; i < arr.length; i++) {
console.log(1111) // 注意此處瞬間執行完,執行了arr.length -1 次
p = p.then(function () {
return setTime(arr[i]);
})
}
}
let arr = ['a','b','c']
promiseChain(arr);
如下修改請注意:
如果不使用let 或 const 而是使用的var,那麼promise鍊執行結果為arr[length-1]的值,是以此處的let和const 塊作用域也相當關鍵
for (var i = 0; i < arr.length; i++) {
var m = arr[i]
p = p.then(function () {
return setTime(m);
})
}
如果使用async和await,整個for循環在執行到await時都會等待
async function message() {
for (let item of arr) {
console.log(item)
await new Promise(function (resolve, reject) {
setTimeout(() => {
console.log(item)
resolve();
}, 1000)
})
}
}
message()
列印結果:
立即列印a——1s後,列印a b——1s後列印b c——1s後列印c