簡體   English   中英

我如何等待所有動態嵌套承諾結束?

[英]How can I wait for all dynamic nested promises to end?

我有以下代碼:

  getContent() {

    fetch(this.url)
    .then((response) => response.text())
    .then((data) => {
      let jsonData = JSON.parse(data);

      for (let i=0; i<jsonData.results.length; i++) {

        this.fetchPokemon(jsonData.results[i].url)
        .then(() => console.log("end"));
      }

      console.log(calls);
    })
    .catch((error) => console.log(error));
  }

  fetchPokemon(url) {
    return new Promise((resolve) => {
      fetch(url)
      .then((response) => response.text())
      .then((data) => {
        let jsonData = JSON.parse(data);

        let types = new Array();
        for (let i=0; i<jsonData.types.length; i++) {
          types.push(jsonData.types[i].type.name);
        }

        this.pokemons.push(new Pokemon(jsonData.name, types, jsonData.weight, jsonData.sprites.front_default));
        resolve();
      })
      .catch((error) => console.log(error));
    });
  }

這是我正在處理的 JS class 中的兩個函數。 如您所見,function getContent()執行fetch()並從 API 獲取一些信息。這些信息包含更多 URL,我也想在其中獲取一些信息,因此我放置了一個for循環來執行第二個 function fetchPokemon(url)這將再次調用 API 以獲取一些其他信息。

我正在努力了解如何從getContent()中檢測到循環中的所有Promises都得到正確解析,以便我可以繼續執行其他功能。 感覺Promise.all可以幫忙,但是沒成功。

我怎樣才能做到這一點?

促進可重用性

盡量不要在課堂上投入太多。 功能應該圍繞可移植性進行設計,使它們更容易重用和測試。

function getJson(url) {
 return fetch(url)
   .then((response) => response.text())
   .then((data) => JSON.parse(data))
}

現在 class 可以保持精簡,程序中需要 JSON 的任何其他部分也可以從分離的 function 中受益 -

class MyClass {
  constructor () {
    // ...
  }
  async getContent () {
    const data = await getJson(this.url)
    // ...
  }
}

轉到fetchPokemon ,看看那個,我們需要更多的 JSON! 獲取單個 Pokemon 是一項簡單的任務,只需要輸入url並返回一個新的Pokemon 沒有理由將它與 class 邏輯糾纏在一起——

async function fetchPokemon(url) {
  const { name, types, weight, sprites } = await getJson(url)
  return new Pokemon(
    name,
    types.map(t => t.name),
    weight,
    sprites.front_default
  )
}

現在我們可以完成 class 的getContent方法 -

class MyClass {
  constructor () {
    // ...
  }
  async getContent () {
    const { results } = await getJson(this.url)
    return Promise.all(results.map(item => fetchPokemon(item.url)))
  }
}

揭開面紗

現在我們可以清楚地看到getContent只不過是一個簡單的 function 請求多個pokemon。 如果僅僅是這樣,我也會將其設為通用 function -

async function fetchAllPokemon(url) {
  const { results } = await getJson(url)
  return Promise.all(results.map(item => fetchPokemon(item.url)))
}

對 class 的需求基本上已經消失。 您沒有共享它的其他部分,但您的程序已簡化為三 (3) 個函數getJsonfetchPokemonfetchAllPokemon所有這些都是可重用且易於測試的,以及一個非常簡單的MyClass包裝器 -

function getJson(...) { ... }
async function fetchPokemon(...) { ... }
async function fetchAllPokemon(...) { ... }

class MyClass {
  constructor() {
    // ...
  }
  getContent() {
    return fetchAllPokemon(this.url)
  }
}

促進多功能性

我還應該提到getJson可以簡化,因為fetch響應包含一個.json方法。 為了使其更有用,您可以添加第二個options參數 -

function getJson(url, options = {}) {
  return fetch(url, options).then(res => res.json())
}

不要吞下錯誤

最后,不要在通用函數或 class 方法上附加.catch錯誤處理程序。 錯誤會自動冒出並為調用者保留做更有意義的事情的機會 -

fetchAllPokemon(someUrl)
  .then(displayResult)
  .catch(handleError)

或者,如果您仍計划使用 class -

const foobar = new MyClass(...)
foobar
  .getContent()
  .then(displayResult)
  .catch(handlerError)

縮小

過去,類用於通過封裝邏輯、變量和方法來組織程序。 通常 class 結構會導致難以管理和難以測試的層次結構。 現在有了通過importexport的模塊支持,你可以編寫“扁平化”的程序,功能和組件可以以靈活的方式組合。 考慮使用我們上面編寫的函數的這個 ExpressJS 示例 -

import express from "express"
import { fetchPokemon, fetchAllPokemon } from "./api.js"

app.get("/pokemon", (res, res, next) => {
  fetchAllPokemon(`https://someurl/`)
    .then(data => res.json(data))
    .then(_ => next())
    .catch(err => next(err))
}

app.get("/pokemon/:id",(req,res,next)=> {
  fetchPokemon(`https://someurl/${req.params.id}`)
    .then(data => res.json(data))
    .then(_ => next())
    .catch(err => next(err))
}

或者使用asyncawait更好,因為錯誤會自動冒出來 -

import express from "express"
import { fetchPokemon, fetchAllPokemon } from "./api.js"

app.get("/pokemon", async (res, res) => {
  const data = await fetchAllPokemon(`https://someurl/`)
  res.json(data)
}

app.get("/pokemon/:id", async (res, res) => {
  const data = await fetchPokemon(`https://someurl/${req.params.id}`)
  res.json(data)
}

嘗試像這樣使用Promise.all

  getContent() {
    fetch(this.url)
    .then((response) => response.text())
    .then((data) => {
      let jsonData = JSON.parse(data);

      const promises = [];
      for (let i=0; i<jsonData.results.length; i++) {

        promises.push(this.fetchPokemon(jsonData.results[i].url));
      }

      return Promise.all(promises);
    })
    .then((results) => {
      console.log(results);
    })
    .catch((error) => console.log(error));
  }

所以基本上你只需將所有 Promise 推送到數組,然后應用Promise.all來等待它們解決。

暫無
暫無

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

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