javascript - Node 在同步安装后找不到某些模块

标签 javascript node.js asynchronous npm npm-install

我有一个在启动时同步安装非内置模块的脚本,如下所示

const cp = require('child_process')

function requireOrInstall (module) {
  try {
    require.resolve(module)
  } catch (e) {
    console.log(`Could not resolve "${module}"\nInstalling`)
    cp.execSync(`npm install ${module}`)
    console.log(`"${module}" has been installed`)
  }
  console.log(`Requiring "${module}"`)
  try {
    return require(module)
  } catch (e) {
    console.log(require.cache)
    console.log(e)
  }
}

const http    = require('http')
const path    = require('path')
const fs      = require('fs')
const ffp     = requireOrInstall('find-free-port')
const express = requireOrInstall('express')
const socket  = requireOrInstall('socket.io')
// List goes on...

当我卸载模块时,当我再次启动服务器时它们会成功安装,这正是我想要的。但是,当我卸载列表中使用函数 requireOrInstall 的前两个模块时,脚本开始抛出 Cannot find module 错误。没错,只有当脚本必须安装第一个或前两个模块时才会出现错误,而不是只有第二个模块需要安装时才会出现。

在此示例中,当我卸载 find-free-port 时将抛出错误,除非我将其 require 至少向下移动一个位置¯\_(• _ •)_/¯

我还尝试在同步安装后直接添加延迟,以通过以下两行为其提供更多的呼吸时间:

var until = new Date().getTime() + 1000
while (new Date().getTime() < until) {}

暂停就在那里。它没有解决任何问题。

@velocityzen came with the idea to check the cache ,我现在已将其添加到脚本中。它没有显示出任何异常。

@vaughan's comment on another question请注意,当两次需要一个模块时会发生这个确切的错误。我已将脚本更改为使用 require.resolve(),但错误仍然存​​在。

有人知道是什么原因造成的吗?

编辑

既然问题已经得到解答,我将发布一行(139 个字符!)。它没有全局定义 child_modules,没有最后一个 try-catch,也没有在控制台中记录任何内容:

const req=async m=>{let r=require;try{r.resolve(m)}catch(e){r('child_process').execSync('npm i '+m);await setImmediate(()=>{})}return r(m)}

函数的名称是req(),可以像在@alex-rokabilis' answer 中那样使用。 .

最佳答案

npm install 之后的require 操作似乎需要一定的延迟。 此外,Windows 中的问题更严重,如果模块需要 npm 安装,它将总是失败。 就像在特定事件快照中已经知道哪些模块是必需的,哪些不是。可能这就是评论中提到 require.cache 的原因。不过,我建议您检查以下 2 个解决方案。

1) 使用延迟

const cp = require("child_process");

const requireOrInstall = async module => {
  try {
    require.resolve(module);
  } catch (e) {
    console.log(`Could not resolve "${module}"\nInstalling`);
    cp.execSync(`npm install ${module}`);
    // Use one of the two awaits below
    // The first one waits 1000 milliseconds
    // The other waits until the next event cycle
    // Both work
    await new Promise(resolve => setTimeout(() => resolve(), 1000));
    await new Promise(resolve => setImmediate(() => resolve()));
    console.log(`"${module}" has been installed`);
  }
  console.log(`Requiring "${module}"`);
  try {
    return require(module);
  } catch (e) {
    console.log(require.cache);
    console.log(e);
  }
}

const main = async() => {
  const http = require("http");
  const path = require("path");
  const fs = require("fs");
  const ffp = await requireOrInstall("find-free-port");
  const express = await requireOrInstall("express");
  const socket = await requireOrInstall("socket.io");
}

main();

await 总是需要一个 promise 才能工作,但不需要显式创建一个,因为 await 会把它正在等待的任何东西包装在一个 promise 中,如果它不是的话。我递给了一个。

2) 使用集群

const cp = require("child_process");

function requireOrInstall(module) {
  try {
    require.resolve(module);
  } catch (e) {
    console.log(`Could not resolve "${module}"\nInstalling`);
    cp.execSync(`npm install ${module}`);
    console.log(`"${module}" has been installed`);
  }
  console.log(`Requiring "${module}"`);
  try {
    return require(module);
  } catch (e) {
    console.log(require.cache);
    console.log(e);
    process.exit(1007);
  }
}

const cluster = require("cluster");

if (cluster.isMaster) {
  cluster.fork();
  cluster.on("exit", (worker, code, signal) => {
    if (code === 1007) {
      cluster.fork();
    }
  });
} else if (cluster.isWorker) {
  // The real work here for the worker

  const http = require("http");
  const path = require("path");
  const fs = require("fs");
  const ffp = requireOrInstall("find-free-port");
  const express = requireOrInstall("express");
  const socket = requireOrInstall("socket.io");

  process.exit(0);
}

这里的想法是在缺少模块的情况下重新运行该过程。通过这种方式,我们完全重现了手动 npm install,正如您所猜测的那样!此外,它似乎同步而不是第一个选项,但有点复杂。

关于javascript - Node 在同步安装后找不到某些模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53270058/

相关文章:

ios - 手动 KVO 通知导致串行队列崩溃

c# - 我在哪里可以找到异步队列的当前内容?

javascript - beforeunload on IE 11 - 不提示用户不起作用

javascript - meteor ReactiveVar - TypeError : Cannot call method 'set' of undefined

jquery - Node.js - 使用 json.stringify 时主体困惑

javascript - 运行时出现循环错误 'npm start'?

C# - 将数据传递给 JS 并等待结果进行处理

javascript - 变量 getElementbyId 内部函数

javascript - 在 AngularJS 中在运行时加载 JavaScript

node.js - 无法访问虚拟机内的node.js Web服务器