簡體   English   中英

使用帶有promise的forEach(),而訪問前一個promise會導致.then()鏈?

[英]use forEach() with promises while access previous promise results in a .then() chain?

我有承諾的以下功能:

const ajaxRequest = (url) => {
  return new Promise(function(resolve, reject) {
    axios.get(url)
      .then((response) => {
        //console.log(response);
        resolve(response);
      })
      .catch((error) => {
        //console.log(error);
        reject();
      });
  });
}


const xmlParser = (xml) => {
  let { data } = xml;
  return new Promise(function(resolve, reject) {
    let parser = new DOMParser();
    let xmlDoc = parser.parseFromString(data,"text/xml");

    if (xmlDoc.getElementsByTagName("AdTitle").length > 0) {
      let string = xmlDoc.getElementsByTagName("AdTitle")[0].childNodes[0].nodeValue;
      resolve(string);
    } else {
      reject();
    }
  });
}

我正在嘗試為JSON數組中的每個對象應用這些函數:

const array = [{"id": 1, "url": "www.link1.com"}, {"id": 1, "url": "www.link2.com"}]

我提出了以下解決方案:

function example() {
    _.forEach(array, function(value) {
        ajaxRequest(value.url)
            .then(response => {
                xmlParser(response)
            .catch(err => {
                console.log(err);
            });
        });
    }
 }

我想知道這個解決方案是否可以接受兩件事:

  1. 在以下事項中對承諾申請Each()是一種好的做法。

  2. 有沒有更好的方法將先前的promise結果作為then()鏈中的參數傳遞? (我正在通過response )。

您可以使用.reduce()來訪問以前的Promise

function example() {
    return array.reduce((promise, value) =>
       // `prev` is either initial `Promise` value or previous `Promise` value
       promise.then(prev => 
         ajaxRequest(value.url).then(response => xmlParser(response))
       )
    , Promise.resolve())
 }
 // though note, no value is passed to `reject()` at `Promise` constructor calls
 example().catch(err => console.log(err)); 

注意,在ajaxRequest函數中不需要Promise構造函數。

const ajaxRequest = (url) => 
    axios.get(url)
      .then((response) => {
        //console.log(response);
        return response;
      })
      .catch((error) => {
        //console.log(error);
      });

您提供的代碼的唯一問題是xmlParser的結果丟失,forEach循環只是迭代但不存儲結果。 為了保持結果,你需要使用Array.map作為結果得到Promise,然后Promise.all等待並將所有結果都放到數組中。

我建議使用ES2017的async / await來簡化處理promises。 由於提供的代碼已經使用了箭頭函數,這需要轉換為較舊的瀏覽器兼容性,因此您可以添加轉換插件以支持ES2017。

在這種情況下,您的代碼將是:

function example() {
  return Promise.all([
    array.map(async (value) => {
      try {
        const response = await ajaxRequest(value.url);
        return xmlParser(response);
      } catch(err) {
        console.error(err);
      }
    })
  ])
}

上面的代碼將並行運行所有請求,並在所有請求完成后返回結果。 您可能還希望逐個觸發和處理請求,如果這是您的問題,這也將提供對先前承諾結果的訪問:

async function example(processResult) {
  for(value of array) {
    let result;
    try {
      // here result has value from previous parsed ajaxRequest.
      const response = await ajaxRequest(value.url);
      result = await xmlParser(response);
      await processResult(result);
    } catch(err) {
      console.error(err);
    }
  }
}

另一個解決方案是使用Promise.all來做這件事,我認為這是一個比循環ajax請求更好的解決方案。

const array = [{"id": 1, "url": "www.link1.com"}, {"id": 1, "url": "www.link2.com"}]

function example() {
    return Promise.all(array.map(x => ajaxRequest(x.url)))
        .then(results => {
            return Promise.all(results.map(data => xmlParser(data)));
        });
}

example().then(parsed => {
    console.log(parsed); // will be an array of xmlParsed elements
});

有沒有更好的方法將先前的promise結果作為then()鏈中的參數傳遞?

事實上,您可以按任何順序和代碼的任何位置鏈接和解決承諾。 一個通用規則 - 使用thencatch分支的任何鏈式承諾只是新的承諾,應該在以后鏈接。

但沒有限制。 使用循環,最常見的解決方案是reduce左側foldl,但您也可以使用簡單的let -variable並重新分配新的承諾。

例如,您甚至可以設計延遲的承諾鏈:

function delayedChain() {
  let resolver = null
  let flow = new Promise(resolve => (resolver = resolve));
  for(let i=0; i<100500; i++) {
    flow = flow.then(() => {
      // some loop action
    })
  }

  return () => {
    resolver();
    return flow;
  }
}

(delayedChain())().then((result) => {
  console.log(result)
})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM