Home > Net >  Promise.all fetch continue executing after throwing error?
Promise.all fetch continue executing after throwing error?

Time:07-01

I am trying to fetch JSON data from the WordPress Developer Reference site. I need to search a keyword without knowing if it's a function, class, hook, or method, which is part of the url I need to fetch. So I'm using Promise.all to cycle through all possible urls. It works if the response.status <= 299, throwing the error immediately, and if the response is ok, then it continues to .then. Fine, but occasionally it will return an ok status if the JSON exists and only returns an empty array. So I need to check if the JSON data is an empty array, which I can't seem to do in the first part. I can only check in the second part as far as I know. And if it throws the error it doesn't continue trying the other urls. Any suggestions?

var keyword = 'AtomParser';
const refs = ['function', 'hook', 'class', 'method'];

// Store the promises
let promises = [];

// Cycle through each type until we find one we're looking for
for (let t = 0; t < refs.length; t  ) {
  const url =
    'https://developer.wordpress.org/wp-json/wp/v2/wp-parser-'  
    refs[t]  
    '?search='  
    keyword;
  // console.log(url);
  promises.push(fetch(url));
}
Promise.all(promises)
  .then(function(response) {
    console.log(response[0]);

    // Get the status
    console.log('Status code: '   response[0].status);
    if (response[0].status <= 299) {
      // The API call was successful!
      return response[0].json();
    } else {
      throw new Error('Broken link status code: '   response[0].status);
    }
  })
  .then(function(data) {
    // This is the HTML from our response as a text string
    console.log(data);

    // Make sure we have data
    if (data.length == 0) {
      throw new Error('Empty Array');
    }

    // ref
    const reference = data[0];

    // Only continue if not null or empty
    if (reference !== null && reference !== undefined && data.length > 0) {
      // Success
      // Return what I want from the reference
    }
  })
  .catch(function handleError(error) {
    console.log('Error'   error);
  });

Is there some way to get the JSON data in the first part so I can check if it's in an array while I'm checking the response status?

CodePudding user response:

I would recommend encapsulating the success / failure logic for individual requests, then you can determine all the resolved and rejected responses based on the result of that encapsulation.

For example

const checkKeyword = async (ref, keyword) => {
  const params = new URLSearchParams({ search: keyword });
  const res = await fetch(
    `https://developer.wordpress.org/wp-json/wp/v2/wp-parser-${encodeURIComponent(
      ref
    )}?${params}`
  );

  if (!res.ok) {
    throw new Error(`${res.status}: ${await res.text()}`);
  }

  const data = await res.json();

  if (data.length === 0) {
    throw new Error(`Empty results for '${ref}'`);
  }

  return { ref, data };
};

Now you can use something like Promise.any() or Promise.allSettled() to find the first successful request or all successful requests, respectively

const keyword = "AtomParser";
const refs = ["function", "hook", "class", "method"];

const promises = refs.map((ref) => checkKeyword(ref, keyword));

// First success
Promise.any(promises)
  .then(({ ref, data }) => {
    console.log(ref, data);
  })
  .catch(console.error);

// All successes
Promise.allSettled(promises)
  .then((responses) =>
    responses.reduce(
      (arr, { status, value }) =>
        status === "fulfilled" ? [...arr, value] : arr,
      []
    )
  )
  .then((results) => {
    // results has all the successful responses
  });

CodePudding user response:

wp module

@Phil's answer puts you on the right track but I want to expand on some of his ideas. Use of URLSearchParamas is great but you can improve by using the high-level URL API and forego encodeURIComponent and constructing search params manually. Notice I'm putting this code in its own wp module so I can separate concerns more easily. We don't want all of this code leaking into your main program.

// wp.js

import { fetch } from "whatwg-fetch" // or your chosen implementation

const baseURL = "https://developer.wordpress.org"

async function search1(path, query) {
  const u = new URL(path, baseURL)
  u.searchParams.set("search", query)
  const result = await fetch(u)
  if (!result.ok) throw Error(`Search failed (${result.status}): ${u}`)
  return result.json()
}

search1 searches one path, but we can write search to search all the necessary paths. I don't think there's any reason to get fancy with each path here, so just write them out -

// wp.js (continued)

function search(query) {
  const endpoints = [
    "/wp-json/wp/v2/wp-parser-function",
    "/wp-json/wp/v2/wp-parser-hook",
    "/wp-json/wp/v2/wp-parser-class",
    "/wp-json/wp/v2/wp-parser-method"
  ]
  return Promise
    .all(endpoints.map(e => search1(e, query)))
    .then(results => results.flat())
}

export { search }

main module

Notice we only exported search as search1 is internal to the wp module. Let's see how we can use it in our main module now -

// main.js

import { search } from "./wp.js"

for (const result of await search("database"))
  if(result.guid.rendered)
    console.log(`${result.title.rendered}\n${result.guid.rendered}\n`)

In this example, we first search for "database" -

wp_should_replace_insecure_home_url()
https://developer.wordpress.org/reference/functions/wp_should_replace_insecure_home_url/

wp_delete_signup_on_user_delete()
https://developer.wordpress.org/reference/functions/wp_delete_signup_on_user_delete/

get_post_datetime()
https://developer.wordpress.org/reference/functions/get_post_datetime/

wp_ajax_health_check_get_sizes()
https://developer.wordpress.org/reference/functions/wp_ajax_health_check_get_sizes/

wp_should_replace_insecure_home_url
https://developer.wordpress.org/reference/hooks/wp_should_replace_insecure_home_url/

comments_pre_query
https://developer.wordpress.org/reference/hooks/comments_pre_query/

users_pre_query
https://developer.wordpress.org/reference/hooks/users_pre_query/

WP_Object_Cache
http://developer.wordpress.org/reference/classes/wp_object_cache/

wpdb
http://developer.wordpress.org/reference/classes/wpdb/

WP_REST_Menu_Items_Controller::prepare_item_for_database()
https://developer.wordpress.org/reference/classes/wp_rest_menu_items_controller/prepare_item_for_database/

WP_REST_Global_Styles_Controller::prepare_item_for_database()
https://developer.wordpress.org/reference/classes/wp_rest_global_styles_controller/prepare_item_for_database/

WP_REST_Menus_Controller::prepare_item_for_database()
https://developer.wordpress.org/reference/classes/wp_rest_menus_controller/prepare_item_for_database/

WP_REST_Templates_Controller::prepare_item_for_database()
https://developer.wordpress.org/reference/classes/wp_rest_templates_controller/prepare_item_for_database/

WP_REST_Application_Passwords_Controller::prepare_item_for_database()
https://developer.wordpress.org/reference/classes/wp_rest_application_passwords_controller/prepare_item_for_database/

wpdb::db_server_info()
https://developer.wordpress.org/reference/classes/wpdb/db_server_info/

WP_REST_Attachments_Controller::insert_attachment()
https://developer.wordpress.org/reference/classes/wp_rest_attachments_controller/insert_attachment/

WP_Debug_Data::get_database_size()
https://developer.wordpress.org/reference/classes/wp_debug_data/get_database_size/

WP_REST_Meta_Fields::update_multi_meta_value()
https://developer.wordpress.org/method/wp_rest_meta_fields/update_multi_meta_value/

another search example

Now let's search for "image" -

for (const result of await search("image"))
  if(result.guid.rendered)
    console.log(`${result.title.rendered}\n${result.guid.rendered}\n`)
get_adjacent_image_link()
https://developer.wordpress.org/reference/functions/get_adjacent_image_link/

get_next_image_link()
https://developer.wordpress.org/reference/functions/get_next_image_link/

get_previous_image_link()
https://developer.wordpress.org/reference/functions/get_previous_image_link/

wp_robots_max_image_preview_large()
https://developer.wordpress.org/reference/functions/wp_robots_max_image_preview_large/

wp_getimagesize()
https://developer.wordpress.org/reference/functions/wp_getimagesize/

is_gd_image()
https://developer.wordpress.org/reference/functions/is_gd_image/

wp_show_heic_upload_error()
https://developer.wordpress.org/reference/functions/wp_show_heic_upload_error/

wp_image_src_get_dimensions()
https://developer.wordpress.org/reference/functions/wp_image_src_get_dimensions/

wp_image_file_matches_image_meta()
https://developer.wordpress.org/reference/functions/wp_image_file_matches_image_meta/

_wp_check_existing_file_names()
https://developer.wordpress.org/reference/functions/_wp_check_existing_file_names/

edit_custom_thumbnail_sizes
https://developer.wordpress.org/reference/hooks/edit_custom_thumbnail_sizes/

get_header_image_tag_attributes
https://developer.wordpress.org/reference/hooks/get_header_image_tag_attributes/

image_editor_output_format
https://developer.wordpress.org/reference/hooks/image_editor_output_format/

wp_image_src_get_dimensions
https://developer.wordpress.org/reference/hooks/wp_image_src_get_dimensions/

wp_get_attachment_image
https://developer.wordpress.org/reference/hooks/wp_get_attachment_image/

image_sideload_extensions
https://developer.wordpress.org/reference/hooks/image_sideload_extensions/

wp_edited_image_metadata
https://developer.wordpress.org/reference/hooks/wp_edited_image_metadata/

wp_img_tag_add_loading_attr
https://developer.wordpress.org/reference/hooks/wp_img_tag_add_loading_attr/

wp_image_file_matches_image_meta
https://developer.wordpress.org/reference/hooks/wp_image_file_matches_image_meta/

get_custom_logo_image_attributes
https://developer.wordpress.org/reference/hooks/get_custom_logo_image_attributes/

Custom_Image_Header
http://developer.wordpress.org/reference/classes/custom_image_header/

WP_Image_Editor_Imagick
http://developer.wordpress.org/reference/classes/wp_image_editor_imagick/

WP_Embed
http://developer.wordpress.org/reference/classes/wp_embed/

WP_Image_Editor
http://developer.wordpress.org/reference/classes/wp_image_editor/

WP_Customize_Background_Image_Setting
http://developer.wordpress.org/reference/classes/wp_customize_background_image_setting/

WP_Customize_Header_Image_Setting
http://developer.wordpress.org/reference/classes/wp_customize_header_image_setting/

WP_Image_Editor_GD
http://developer.wordpress.org/reference/classes/wp_image_editor_gd/

WP_Customize_Header_Image_Control
http://developer.wordpress.org/reference/classes/wp_customize_header_image_control/

WP_REST_Server::add_image_to_index()
https://developer.wordpress.org/reference/classes/wp_rest_server/add_image_to_index/

WP_REST_URL_Details_Controller::get_image()
https://developer.wordpress.org/reference/classes/wp_rest_url_details_controller/get_image/

WP_Image_Editor::get_default_quality()
https://developer.wordpress.org/reference/classes/wp_image_editor/get_default_quality/

WP_Theme_JSON::get_blocks_metadata()
https://developer.wordpress.org/reference/classes/wp_theme_json/get_blocks_metadata/

WP_Image_Editor_Imagick::pdf_load_source()
https://developer.wordpress.org/reference/classes/wp_image_editor_imagick/pdf_load_source/

WP_Image_Editor_Imagick::write_image()
https://developer.wordpress.org/reference/classes/wp_image_editor_imagick/write_image/

WP_Image_Editor_Imagick::maybe_exif_rotate()
https://developer.wordpress.org/reference/classes/wp_image_editor_imagick/maybe_exif_rotate/

WP_Image_Editor_Imagick::make_subsize()
https://developer.wordpress.org/reference/classes/wp_image_editor_imagick/make_subsize/

WP_Image_Editor_GD::make_subsize()
https://developer.wordpress.org/reference/classes/wp_image_editor_gd/make_subsize/

empty search result

Searching for "zzz" will yield no results -

for (const result of await search("zzz"))
  if(result.guid.rendered)
    console.log(`${result.title.rendered}\n${result.guid.rendered}\n`)
<empty result>
  • Related