【问题标题】:Nodejs synchronous loops block executionNodejs 同步循环阻塞执行
【发布时间】:2018-07-09 06:51:30
【问题描述】:

当我尝试在后台运行一个函数时,它会阻止所有其他请求,直到它完成...

例如,如果我执行该函数,然后尝试向从数据库返回一些信息的路由发出 get 请求,那么只有在该函数执行完成后才会收到响应,我不明白为什么。

这是我在后台运行的函数的基本结构(它从页面中查找第 3 方请求,然后为每个请求查找发起方请求):

  const thirdPartyReq = [];
  let allRequests = [];

  const findInitiatorReq = async () => {
    allRequests = allRequests.reverse();
    for(const [_, request] of thirdPartyReq.entries()) {
      if(!request["Initiator Request"]) {
        const fullRequest = request['Request URL'];
        const parseUrl = new URL(fullRequest);
        let hostname = parseUrl.hostname || null;

        const domain = await extractDomain(hostname);
        let pathname = parseUrl.pathname || null;
        hostname = hostname.replace(/www./g, '')
        let checkUrl;

        const domainIndex = hostname.indexOf(domain) - 1;
        const subdomain = (hostname.substr(0, domainIndex));
        const queryString = parseUrl.search || '';
        const noProtocol = hostname + pathname + queryString;
        const noQueryString = hostname + pathname;
        const requestProcessing = [fullRequest, noProtocol, noQueryString, hostname];

        const requestIndex = allRequests.findIndex((el) => {
          return (el.url == request['Request URL'] && el.thirdParty);
        });

        for(const [_, query] of requestProcessing.entries()) {
          for(const [index, checkRequest] of allRequests.entries()) {
            if(index > requestIndex) {
              if(checkRequest.content && checkRequest.content.body) {
                const contentBody = checkRequest.content.body;
                if(contentBody.includes(query)) {
                  request['Initiator Request'] = checkRequest.url;
                }
              }
            }
          }
        }
      }
    }
  }

  for(const [pageIndex, page] of results.entries()) {
    const pageUrl = page.url;
    const requests = page.requests;
    const savedRequestUrls = [];
    let parseUrl = new URL(pageUrl);
    let hostname = parseUrl.hostname;
    let requestsCounter = 0;

    const pageDomain = await extractDomain(hostname);

    if(!urlList.includes(pageUrl)) {
      crawledUrls.push(pageUrl);
    }

    for(const [_, request] of Object.entries(requests)) {
      if(request.url.indexOf('data:') == -1) {
        parseUrl = new URL(request.url);
        hostname = parseUrl.hostname;
        let requestDomain = await extractDomain(hostname);

        const reqObj = await findThirdPartyReq(pageUrl, request, requestDomain);
        if(reqObj != null) {
          request.thirdParty = true;
          savedRequestUrls.push(reqObj);
        }

        // Store all requests that have a domain
        if(requestDomain) {
          request.page = pageUrl;
          allRequests.push(request);
          requestsCounter++;
        }
      }
    }

    findInitiatorReq();
  }

我注意到,如果我删除这部分代码,一切都会正常运行:

    for(const [_, query] of requestProcessing.entries()) {
      for(const [index, checkRequest] of allRequests.entries()) {
        if(index > requestIndex) {
          if(checkRequest.content && checkRequest.content.body) {
            const contentBody = checkRequest.content.body;
            if(contentBody.includes(query)) {
              request['Initiator Request'] = checkRequest.url;
            }
          }
        }
      }
    }

这是调用函数的路由:

router.get('/cookies',async (req, res) => {
   res.status(200).send(true);
   const cookies = await myFunc();
}

谁能告诉我为什么该函数在返回响应之前会阻塞所有内容,我该如何解决这个问题?

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    嗯,显然你有一个synchronous 循环,当然,它会阻止执行。 无论如何它最终会阻止它,因为它必须执行几个繁重的操作。对客户端的响应已发送,但您仍继续处理某些内容,因此其他请求将不得不等待。

    一个可能的解决方案可能是触发另一个node 进程并处理那里的东西(类似于浏览器中的WebWorker

    你可以试试这个库:async,其中有一个eachSeries 方法,专门用于处理大块数据/数组。有关详细信息,请参阅文档

    【讨论】:

    • 如果你使用 NodeJS,你不需要任何外部模块。请检查我的答案
    • 嗯,这样可以节省一些时间。也许 OP 需要很多这样的异步操作,实际上他最好只委托给一个库。
    【解决方案2】:

    这里显而易见的答案是将您的函数转换为异步函数。 StackOverflow 上已经有多个关于该主题的答案。 要点:在处理一些繁重的任务时使用异步函数。请记住,NodeJS 是单线程的,因此同步函数会阻塞其他函数的执行这一事实在某种程度上是意料之中的。

    实现异步功能需要用到的工具有:async/await(在最新的 NodeJS LTS 中不包含库/转译)和 Promises。忘记回调,因为它们是一个非常糟糕的设计。

    js中如何使用async/await:

    如何使用 Promises 以及它们是什么:

    【讨论】:

    • 我已经在我的项目中使用 async/await,但我不知道如何处理给定示例中的 for 循环......我是否必须完全摆脱它们或我应该改变什么?
    • 您能否使用可以在 StackOverflow 上共享的虚假数据来增强您的代码 sn-p?这样我们就可以更全面地了解您想要实现的目标,以及如何在可能的情况下使其表现更好。
    猜你喜欢
    • 2018-07-29
    • 2020-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-06
    • 2020-02-22
    • 1970-01-01
    相关资源
    最近更新 更多