NextJS API 路由在收到数据之前返回?

Dar*_*ley 3 event-loop settimeout async-await es6-promise next.js

我不确定这里发生了什么事。我在 NextJS 中设置了一个 API 路由,该路由在数据加载之前返回。有人可以指出这里有任何错误吗?

我有一个调用 makeRequest() 中的数据的函数:

export async function getVendors() {
  const vendors = await makeRequest(`Vendor.json`);
  console.log({ vendors });
  return vendors;
}
Run Code Online (Sandbox Code Playgroud)

然后路由:/api/vendors.js

export default async (req, res) => {
  const response = await getVendors();
  return res.json(response);
};
Run Code Online (Sandbox Code Playgroud)

这是 makeRequest 函数:

const makeRequest = async (url) => {
  // Get Auth Header
  const axiosConfig = await getHeader();

  // Intercept Rate Limited API Errors & Retry
  api.interceptors.response.use(
    function (response) {
      return response;
    },
    async function (error) {
      await new Promise(function (res) {
        setTimeout(function () {
          res();
        }, 2000);
      });

      const originalRequest = error.config;

      if (error.response.status === 401 && !originalRequest._retry) {
        token[n] = null;
        originalRequest._retry = true;
        const refreshedHeader = await getHeader();
        api.defaults.headers = refreshedHeader;
        originalRequest.headers = refreshedHeader;
        return Promise.resolve(api(originalRequest));
      }
      return Promise.reject(error);
    }
  );

  // Call paginated API and return number of requests needed.
  const getQueryCount = await api.get(url, axiosConfig).catch((error) => {
    throw error;
  });
  const totalItems = parseInt(getQueryCount.data['@attributes'].count);
  const queriesNeeded = Math.ceil(totalItems / 100);

  // Loop through paginated API and push data to dataToReturn
  const dataToReturn = [];

  for (let i = 0; i < queriesNeeded; i++) {
    setTimeout(async () => {
      try {
        const res = await api.get(`${url}?offset=${i * 100}`, axiosConfig);
        console.log(`adding items ${i * 100} through ${(i + 1) * 100}`);
        const { data } = res;
        const arrayName = Object.keys(data)[1];
        const selectedData = await data[arrayName];
        selectedData.map((item) => {
          dataToReturn.push(item);
        });

        if (i + 1 === queriesNeeded) {
          console.log(dataToReturn);
          return dataToReturn;
        }
      } catch (error) {
        console.error(error);
      }
    }, 3000 * i);
  }
};
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是 getVendors() 在 makeRequest() 完成获取数据之前返回。

Nic*_*ick 5

看来您的问题源于您对 的使用setTimeout。您正在尝试从内部返回数据setTimeout,但由于某些原因,这不起作用。因此,在这个答案中,我将回顾为什么我认为它不适合您以及潜在的解决方案。

setTimeout 和事件循环

看看这段代码,你认为会发生什么?

console.log('start')
setTimeout(() => console.log('timeout'), 1000)
console.log('end')
Run Code Online (Sandbox Code Playgroud)

当您使用 时setTimeout,内部代码将从当前事件循环中拉出以便稍后运行。这就是为什么end在之前记录timeout原因。

所以当你使用setTimeout返回数据时,在超时内的代码开始之前,函数就已经结束了。

如果您是事件循环的新手,这里有一个非常精彩的演讲: https: //youtu.be/cCOL7MC4Pl0

在 setTimeout 内返回

然而,这里还有另一个根本问题。内部返回的数据setTimeout是函数的返回值setTimeout,而不是父函数的返回值。尝试运行这个,你认为会发生什么?

const foo = () => {
  setTimeout(() => {
    return 'foo timeout'
  }, 1000)
}

const bar = () => {
  setTimeout(() => {
    return 'bar timeout'
  }, 1000)
  return 'bar'
}

console.log(foo())
console.log(bar())
Run Code Online (Sandbox Code Playgroud)

这是 a) 上面提到的事件循环和 b) 内部的结果setTimeout,您正在创建一个具有新作用域的新函数。

解决方案

setTimeout如果最后确实需要,请使用 Promise。通过 Promise,您可以使用resolve参数从 setTimeout 中解析外部 Promise。

const foo = () => {
  return new Promise((resolve) => {
    setTimeout(() => resolve('foo'), 1000)
  })
}

const wrapper = async () => {
  const returnedValue = await foo()
  console.log(returnedValue)
}

wrapper()
Run Code Online (Sandbox Code Playgroud)

快速说明

由于您正在调用setTimeout异步函数的内部,因此您可能希望将 setTimeout 移动到它自己的函数中。否则,您将返回一个嵌套的承诺。

// don't do this

const foo = async () => {
  return new Promise((resolve) => resolve(true))
}

// because then the result is a promise
const result = await foo()
const trueResult = await result()
Run Code Online (Sandbox Code Playgroud)