美文网首页
NodeJS执行js文件流程

NodeJS执行js文件流程

作者: orgcheng | 来源:发表于2020-03-27 15:31 被阅读0次

在终端执行node test.js来运行test.js文件,通过抛出的异常日志,来分析nodejs的执行流程

D:\AliOS\HelloWorldTS>node test.js
internal/modules/cjs/loader.js:800
    throw err;
    ^

Error: Cannot find module 'D:\AliOS\HelloWorldTS\test.js'
[90m    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:797:15)[
[90m    at Function.Module._load (internal/modules/cjs/loader.js:690:27)[39m
[90m    at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)[39m
[90m    at internal/main/run_main_module.js:17:11[39m {
  code: [32m'MODULE_NOT_FOUND'[39m,
  requireStack: []
}

先看 internal/main/run_main_module.js

const {
  prepareMainThreadExecution
} = require('internal/bootstrap/pre_execution');

prepareMainThreadExecution(true);

require('internal/modules/cjs/loader').Module.runMain(process.argv[1]);

最后一句,加载internal/modules/cjs/loader并运行Module.runMain方法,传递的参数就是test.js

但是它首先加载了internal/bootstrap/pre_execution的prepareMainThreadExecution方法,并执行。

接下来就看看internal/bootstrap/pre_execution

function prepareMainThreadExecution(expandArgv1 = false) {
    ...
    initializeCJSLoader();
    ...
}

function initializeCJSLoader() {
  const CJSLoader = require('internal/modules/cjs/loader');
  CJSLoader.Module._initPaths();
  // TODO(joyeecheung): deprecate this in favor of a proper hook?
  CJSLoader.Module.runMain =
    require('internal/modules/run_main').executeUserEntryPoint;
}
    
module.exports = {

  ...

  prepareMainThreadExecut
};

到此我们知道,internal/modules/cjs/loader中的Module.runMain方法,是internal/modules/run_main模块的executeUserEntryPoint方法

看下internal/modules/run_main

function executeUserEntryPoint(main = process.argv[1]) {
  const resolvedMain = resolveMainPath(main);
  const useESMLoader = shouldUseESMLoader(resolvedMain);
  if (useESMLoader) {
    runMainESM(resolvedMain || main);
  } else {
    // Module._load is the monkey-patchable CJS module loader.
    Module._load(main, null, true); // 只需要知道,最终走这里就可以了
  }
}

module.exports = {
  executeUserEntryPoint
};

接下来就是执行internal/modules/cjs/loader中的Module._load方法

Module._extensions = ObjectCreate(null);

// Check the cache for the requested file.
// 1. If a module already exists in the cache: return its exports object.
// 2. If the module is native: call
//    `NativeModule.prototype.compileForPublicLoader()` and return the exports.
// 3. Otherwise, create a new module for the file and save it to the cache.
//    Then have it load  the file contents before returning its exports
//    object.
Module._load = function(request, parent, isMain) {
  let relResolveCacheIdentifier;
  if (parent) {
    ... // 这里parent=null,不执行此处代码
  }

  // 开头报错就是这里抛出的
  const filename = Module._resolveFilename(request, parent, isMain);

  // 查找缓存
  const cachedModule = Module._cache[filename];
  if (cachedModule !== undefined) {
    updateChildren(parent, cachedModule, true);
    return cachedModule.exports;
  }
   
  // 加载Native模块
  const mod = loadNativeModule(filename, request, experimentalModules);
  if (mod && mod.canBeRequiredByUsers) return mod.exports;

  // Don't call updateChildren(), Module constructor already does.
  const module = new Module(filename, parent);

  if (isMain) {
    process.mainModule = module;
    module.id = '.';
  }

  Module._cache[filename] = module;
  if (parent !== undefined) {
    relativeResolveCache[relResolveCacheIdentifier] = filename;
  }

  let threw = true;
  try {
    // Intercept exceptions that occur during the first tick and rekey them
    // on error instance rather than module instance (which will immediately be
    // garbage collected).
    if (enableSourceMaps) {
      try {
        module.load(filename);
      } catch (err) {
        rekeySourceMap(Module._cache[filename], err);
        throw err; /* node-do-not-add-exception-line */
      }
    } else {
      module.load(filename);
    }
    threw = false;
  } finally {
    if (threw) {
      delete Module._cache[filename];
      if (parent !== undefined) {
        delete relativeResolveCache[relResolveCacheIdentifier];
      }
    }
  }

  return module.exports;
};

// Given a file name, pass it to the proper extension handler.
Module.prototype.load = function(filename) {
  this.filename = filename;
  this.paths = Module._nodeModulePaths(path.dirname(filename));

  const extension = findLongestRegisteredExtension(filename);
  // allow .mjs to be overridden
  if (filename.endsWith('.mjs') && !Module._extensions['.mjs']) {
    throw new ERR_REQUIRE_ESM(filename);
  }
  // 这里是调用对应的扩展名方法
  Module._extensions[extension](this, filename);
  this.loaded = true;
  ...
}  
    
// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
  if (filename.endsWith('.js')) {
    const pkg = readPackageScope(filename);
    // Function require shouldn't be used in ES modules.
    if (pkg && pkg.data && pkg.data.type === 'module') {
      const parentPath = module.parent && module.parent.filename;
      const packageJsonPath = path.resolve(pkg.path, 'package.json');
      throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
    }
  }
  const content = fs.readFileSync(filename, 'utf8');
  module._compile(content, filename);
};
    
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {
  ...

  maybeCacheSourceMap(filename, content, this);
  const compiledWrapper = wrapSafe(filename, content, this);

  ...
  
  const dirname = path.dirname(filename);
  const require = makeRequireFunction(this, redirects);
  let result;
  const exports = this.exports;
  const thisValue = exports;
  const module = this;
  ...
    result = compiledWrapper.call(thisValue, exports, require, module,
                                  filename, dirname);
  ...
  hasLoadedAnyUserCJSModule = true;
  
  return result;
}; 

    
function wrapSafe(filename, content, cjsModuleInstance) {
  if (patched) {
    ...//这里patched为false,表示没有修改过wrapper数组
  }

  let compiled;
  try {
    compiled = compileFunction(
      content,
      filename,
      0,
      0,
      undefined,
      false,
      undefined,
      [],
      [
        'exports',
        'require',
        'module',
        '__filename',
        '__dirname',
      ]
    );
  } catch (err) {
    if (experimentalModules && process.mainModule === cjsModuleInstance)
      enrichCJSError(err);
    throw err;
  }
  return compiled.function;
}    
    
const { compileFunction } = internalBinding('contextify');    

internalBinding就是nodejs的C++代码层暴露到js层的方法,用来加载内置的C++模块

接着看下internal/main/run_main_module方法是在哪里调用的,搜索文件名后发现在node.cc代码中使用到了

// node.cc文件

MaybeLocal<Value> StartMainThreadExecution(Environment* env) {
  // To allow people to extend Node in different ways, this hook allows
  // one to drop a file lib/_third_party_main.js into the build
  // directory which will be executed instead of Node's normal loading.
  if (NativeModuleEnv::Exists("_third_party_main")) {
    return StartExecution(env, "internal/main/run_third_party_main");
  }

  std::string first_argv;
  if (env->argv().size() > 1) {
    first_argv = env->argv()[1];
  }
  ...
  if (!first_argv.empty() && first_argv != "-") {
    return StartExecution(env, "internal/main/run_main_module");
  }
  ...
  return StartExecution(env, "internal/main/eval_stdin");
}


void LoadEnvironment(Environment* env) {
  CHECK(env->is_main_thread());
  // TODO(joyeecheung): Not all of the execution modes in
  // StartMainThreadExecution() make sense for embedders. Pick the
  // useful ones out, and allow embedders to customize the entry
  // point more directly without using _third_party_main.js
  USE(StartMainThreadExecution(env));
}

LoadEnvironment方法是在node_main_instance.cc方法中执行的

int NodeMainInstance::Run() {
  Locker locker(isolate_);
  Isolate::Scope isolate_scope(isolate_);
  HandleScope handle_scope(isolate_);

  int exit_code = 0;
  // TODO 
  std::unique_ptr<Environment> env = CreateMainEnvironment(&exit_code);

  CHECK_NOT_NULL(env);
  Context::Scope context_scope(env->context());

  if (exit_code == 0) {
    {
      InternalCallbackScope callback_scope(
          env.get(),
          Local<Object>(),
          { 1, 0 },
          InternalCallbackScope::kAllowEmptyResource |
              InternalCallbackScope::kSkipAsyncHooks);
      // TODO             
      LoadEnvironment(env.get());
    }

    env->set_trace_sync_io(env->options()->trace_sync_io);

    {
      SealHandleScope seal(isolate_);
      bool more;
      env->performance_state()->Mark(
          node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
      // TODO chengzhen libuv库的试下Nodejs主循环
      do {
        uv_run(env->event_loop(), UV_RUN_DEFAULT);

        per_process::v8_platform.DrainVMTasks(isolate_);

        more = uv_loop_alive(env->event_loop());
        if (more && !env->is_stopping()) continue;

        if (!uv_loop_alive(env->event_loop())) {
          EmitBeforeExit(env.get());
        }

        // Emit `beforeExit` if the loop became alive either after emitting
        // event, or after running some callbacks.
        more = uv_loop_alive(env->event_loop());
      } while (more == true && !env->is_stopping());
      env->performance_state()->Mark(
          node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
    }

    env->set_trace_sync_io(false);
    exit_code = EmitExit(env.get());
  }

  env->set_can_call_into_js(false);
  env->stop_sub_worker_contexts();
  ResetStdio();
  env->RunCleanup();

  // TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really
  // make sense here.
#if HAVE_INSPECTOR && defined(__POSIX__) && !defined(NODE_SHARED_MODE)
  struct sigaction act;
  memset(&act, 0, sizeof(act));
  for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
    if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF)
      continue;
    act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
    CHECK_EQ(0, sigaction(nr, &act, nullptr));
  }
#endif

  RunAtExit(env.get());

  per_process::v8_platform.DrainVMTasks(isolate_);

#if defined(LEAK_SANITIZER)
  __lsan_do_leak_check();
#endif

  return exit_code;
}

相关文章

  • NodeJS执行js文件流程

    在终端执行node test.js来运行test.js文件,通过抛出的异常日志,来分析nodejs的执行流程 先看...

  • 针对java程序员的前端开发学习说明

    Nodejs Nodejs是什么 Nodejs是javascript的执行引擎,提供的js文件作为运行入口。作为生...

  • 大前端

    一、nodejs 进入交互式环境:node 执行js文件:node index.js,.js后缀可以省略。 nod...

  • 我的第一个nodejs(windows)

    1.安装好nodejs 2.用cmd执行命令一个js文件 node D:\Workspace\nodejs\one...

  • 资源打包工具webpack的安装与使用

    1. node.js安装 (1)访问nodejs官网进行下载,并获取到可执行文件。node.js自带npm(包管理...

  • 使用umi脚手架来初始化React项目

    一、开发环境 首先,请安装 NodeJS。NodeJS 是一个 JS 执行环境,umi 基于 JS 编写,并且需要...

  • nodeJS01

    1.nodeJS简介 简单地说node.js就是运行在服务端的JavaScript 2.执行一个最简单的JS文件 ...

  • 使用nodejs创建Marketing Cloud的contac

    源代码如下: 这里我把创建的contact的名称字段硬编码成Jerry4: 使用nodejs执行这个js文件,输出...

  • nodejs01

    (啊我真棒,学完了这节)nodejs是运行在服务端的js.把我们平时用到的js文件放在node上执行,它就是nod...

  • Nodejs Event Loop

    Nodejs 特点 Nodejs 是非阻塞I/O、事件与回调函数、单线程、跨平台 Nodejs 执行流程 Node...

网友评论

      本文标题:NodeJS执行js文件流程

      本文链接:https://www.haomeiwen.com/subject/hkwluhtx.html