这两个代码之间有什么区别?

时间:2018-07-30 14:55:43

标签: javascript nightmare

昨天我问了一个问题。可以解决,谢谢您的回答。 但是我还有另一个关于堆内存不足的问题。

代码说明:  这是用于在网站中自动随机回答单词的代码。在首次循环之前,它们是用于进入目标网站的代码。在下一个循环之前,这些代码用于移动其他页面。然后在第二个循环中,依次检查单选按钮。

我最初是写代码的,但是终端发出了一个错误……。

/Users/Papillon/Documents/lingua3.js:60
                .evaluate(function (){
                 ^

TypeError: nightmare.wait(...).click(...).wait(...).click(...).wait(...).evaluate(...).then(...).evaluate is not a function
    at main (/Users/Papillon/Documents/lingua3.js:60:18)
    at Object.<anonymous> (/Users/Papillon/Documents/lingua3.js:91:1)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
    at startup (internal/bootstrap/node.js:266:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)

这是第一个代码。

const Nightmare = require('nightmare');
const nightmare = Nightmare({show: true});

const LinguaURL = "";
const NumberOfMine = "";
const PwdOfMine = "";

var k,i,j = -1;
var randSelection = Math.floor(Math.random()*5);
var radioSelect = 0;
var selection,progress;

function main() {
  nightmare
    .goto(LinguaURL)
    .wait(1000)
    .type("input[type='text']", NumberOfMine)
    .type("input[type='password']", PwdOfMine)
    .click("input[type='submit']")
    .wait(5000)
    .click('a[href="javascript:document.Study.submit()"]')
    .wait(3000)
    .click("input[type='button']")
    .wait(3000);

    for(i = 3;i<43;i++){

      nightmare
        .click('a[OnClick="unit_view_page(\''+i.toString()+'\');"]')
        .wait(1000);
        for(k = 0; k < 10 * 42;k++){
          j++;

          if(j % 4 == 0){
          nightmare
            .click('input[onclick="select_unit(\'drill\', \''+(1833+j).toString()+'\', \'\');"]')
            .wait(500);

            while(true){

              if(radioSelect == 5)radioSelect = 0;

              nightmare
                .wait(2000)
                .click('input[id="answer_0_' + radioSelect.toString() +'"]')
                .wait(1000)
                .click('input[id="ans_submit"]')
                .wait(1000)
                .evaluate(function (){
                  selection = document.querySelector('.btn btn-answer-view form-font-size');
                  progress = document.querySelector('btn btn-next-problem form-font-size');

                  return (selection != null && progress != null);
                })
                .then((result) => {
                  if(result){
                    return false;
                  }
                })
                .evaluate(function (){
                  selection = document.querySelector('.btn btn-answer-view form-font-size');
                  return selection;
                })
                .then((result) => {

                  radioSelect++;

                  if(selection != null){
                    return true;
                  }else{
                    nightmare
                      .click('input[class="btn btn-next-problem form-font-size"]')
                      .wait(1000);
                    radioSelect = 0;
                    return true;
                  }
                });
          }
          if((k + 1) % 10 == 0){
            break;
          }
          }
        }
      }
      nightmare
        .wait(100)
        .end()
        .then(console.log);
}

main();

出现错误后,我对代码做了一些更改。 第二个代码是这样,

const Nightmare = require('nightmare');
const nightmare = Nightmare({show: true});

const LinguaURL = "";    //site url
const NumberOfMine = ""; //my id
const PwdOfMine = "";    //my password

var k,i,j = -1;
var randSelection = Math.floor(Math.random()*5);
var radioSelect = 0;
var selection,progress;

function main() {
  nightmare
    .goto(LinguaURL)
    .wait(1000)
    .type("input[type='text']", NumberOfMine)
    .type("input[type='password']", PwdOfMine)
    .click("input[type='submit']")
    .wait(5000)
    .click('a[href="javascript:document.Study.submit()"]')
    .wait(3000)
    .click("input[type='button']")
    .wait(3000);

for(i = 3;i<43;i++){

  nightmare
    .click('a[OnClick="unit_view_page(\''+i.toString()+'\');"]')
    .wait(1000);
    for(k = 0; k < 10 * 42;k++){
      j++;

      if(j % 4 == 0){
      nightmare
        .click('input[onclick="select_unit(\'drill\', \''+(1833+j).toString()+'\', \'\');"]')
        .wait(500);

        while(true){

          if(radioSelect == 5)radioSelect = 0;

          nightmare
            .wait(2000)
            .click('input[id="answer_0_' + radioSelect.toString() +'"]')
            .wait(1000)
            .click('input[id="ans_submit"]')
            .wait(1000)
            .evaluate(function (){
              selection = document.querySelector('.btn btn-answer-view form-font-size');
              progress = document.querySelector('btn btn-next-problem form-font-size');

              return (selection != null && progress != null);
            })
            .then((result) => {
              if(result){
                return false; //means break
              }
            });

          nightmare  //difference of the codes
            .evaluate(function (){
              selection = document.querySelector('.btn btn-answer-view form-font-size');
              return (selection != null);
            })
            .then((result) => {

              radioSelect++;

              if(result){
                return true;
              }else{
                nightmare
                  .click('input[class="btn btn-next-problem form-font-size"]')
                  .wait(1000);
                radioSelect = 0;
                return true;
              }
            });
      }
      if((k + 1) % 10 == 0){
        break;
      }
      }
    }
  }
  nightmare
    .wait(100)
    .end()
    .then(console.log);

}

main();

然后,我又遇到另一个错误,内存不足。

<--- Last few GCs --->

[3302:0x102803200]    44201 ms: Scavenge 1389.7 (1424.3) -> 1389.3 (1425.3) MB, 9.4 / 0.0 ms  (average mu = 0.090, current mu = 0.035) allocation failure 
[3302:0x102803200]    48726 ms: Mark-sweep 1390.0 (1425.3) -> 1389.5 (1425.3) MB, 4519.3 / 2.5 ms  (average mu = 0.044, current mu = 0.004) allocation failure scavenge might not succeed


<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x1f7919cc1841]
Security context: 0x2e9fd2b1e6c9 <JSObject>
    1: _send [0x2e9ff5855129] [internal/child_process.js:~636] [pc=0x1f7919cc1cc1](this=0x2e9f3a49c331 <ChildProcess map = 0x2e9f35263999>,message=0x2e9fce5c2fa9 <JSArray[4]>,handle=0x2e9f356822e1 <undefined>,options=0x2e9fce5c3089 <Object map = 0x2e9f35264759>,callback=0x2e9f356822e1 <undefined>)
    2: emit [0x2e9ff585df21] [/Users/Papillon/Documents/node_...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x1000389cc node::Abort() [/Users/Papillon/.nodebrew/current/bin/node]
 2: 0x100038ba8 node::FatalTryCatch::~FatalTryCatch() [/Users/Papillon/.nodebrew/current/bin/node]
 3: 0x1001a9d5a v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/Papillon/.nodebrew/current/bin/node]
 4: 0x100578772 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/Papillon/.nodebrew/current/bin/node]
 5: 0x100577729 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/Papillon/.nodebrew/current/bin/node]
 6: 0x1005753b8 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/Papillon/.nodebrew/current/bin/node]
 7: 0x1005818fc v8::internal::Heap::AllocateRawWithRetry(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/Users/Papillon/.nodebrew/current/bin/node]
 8: 0x10055266a v8::internal::Factory::NewRawOneByteString(int, v8::internal::PretenureFlag) [/Users/Papillon/.nodebrew/current/bin/node]
 9: 0x10067a8f7 v8::internal::String::SlowFlatten(v8::internal::Handle<v8::internal::ConsString>, v8::internal::PretenureFlag) [/Users/Papillon/.nodebrew/current/bin/node]
10: 0x1001c82d5 v8::String::WriteUtf8(char*, int, int*, int) const [/Users/Papillon/.nodebrew/current/bin/node]
11: 0x1000bd5cb node::StringBytes::Write(v8::Isolate*, char*, unsigned long, v8::Local<v8::Value>, node::encoding, int*) [/Users/Papillon/.nodebrew/current/bin/node]
12: 0x1000c1dee int node::StreamBase::WriteString<(node::encoding)1>(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/Papillon/.nodebrew/current/bin/node]
13: 0x1000c62c2 void node::StreamBase::JSMethod<node::LibuvStreamWrap, &(int node::StreamBase::WriteString<(node::encoding)1>(v8::FunctionCallbackInfo<v8::Value> const&))>(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/Papillon/.nodebrew/current/bin/node]
14: 0x1f7919cc1841 
15: 0x1f7919cc1cc1 
Abort trap: 6

有人可以帮助我吗?

1 个答案:

答案 0 :(得分:0)

实际的问题并不在于两个代码之间的差异,而在于代码的编写方式和显示内存问题。

在继续之前,这里有一些链接,请务必阅读它们,否则您将无法理解我在这里所说的内容,从这里了解回调,Promise,Promise Chaining,Promise API和Async Await的知识,其他可用资源,只需选择一个您了解的资源即可。 -https://javascript.info/async

好的,当您完成上述资源并充分了解基础知识之后,我们将继续解决主要问题。

每当编写此nightmare时,都将异步调用它。因此,如果您写nightmare.goto(),它将告诉您噩梦或浏览器实例要访问​​某个网站。而且,如果您编写nightmare.title(),则意味着要给您网站的标题。

但是现在它正忙于访问该网站,那么它将如何知道何时完成加载以及何时获得实际标题?

与for循环相同,您看到的是噩梦达43次,但是它不知道它应该只是执行您现在要求执行的操作还是应该逐个执行。 因此,当您尝试执行太多任务时,会感到困惑,并且不知道先要做哪些工作,然后再做哪些工作。

有异步/等待,承诺和生成器。使用这些技术(现在,让我们说这些只是一些技术),我们可以告诉噩梦一个接一个地运行我们的任务,这样就不会感到困惑。

此外,如果我们看到文档,就会看到每当您对代码执行.evaluate时,它将返回一些数据,除了获取数据或捕获错误之外,您无能为力。因此,evaluate().then().evaluate()基本上是导致您的第一个代码错误/损坏的原因。

让我们一个个地修复第一个代码,而不是固定第二个代码。 首先确保main()是一个异步函数。

async function main() {

那里,该函数现在是异步的。我们可以在此函数内使用await。我们将通过等待nightmare承诺链来做到这一点。

await nightmare
  .goto(LinguaURL)
  .wait(1000)
  // rest of your code

同样需要等待for循环内的噩梦,

await nightmare
  .click('a[OnClick="unit_view_page(\'' + i.toString() + '\');"]')
  .wait(1000);

// rest of the code
await nightmare
 .click('input[onclick="select_unit(\'drill\', \'' + (1833 + j).toString() + '\', \'\');"]')
 .wait(500);

现在进入evaluate问题。我们必须将evaluate调用分开。因此,

await nightmare
  .wait(2000)
  .click('input[id="answer_0_' + radioSelect.toString() + '"]')
  .wait(1000)
  .click('input[id="ans_submit"]')
  .wait(1000)
  .evaluate(function() {
   selection = document.querySelector('.btn btn-answer-view form-font-size');
   progress = document.querySelector('btn btn-next-problem form-font-size');
   return (selection != null && progress != null);
  })
  .then((result) => {
   if (result) {
    return false;
   }
  })
  // we cut the code here

然后将评估移到另一个等待块,

 await nightmare
    .evaluate(function() {
     selection = document.querySelector('.btn btn-answer-view form-font-size');
     return selection;
    })
    .then((result) => {

     radioSelect++;

     if (selection != null) {
      return true;
     } else {
      await nightmare // whenever we call something like this, we await it
       .click('input[class="btn btn-next-problem form-font-size"]')
       .wait(1000);
      radioSelect = 0;
      return true;
     }
    });

当我们等待完成时,我们可以使用它,

await nightmare
  .wait(100)
  .end()
  .then(console.log);

注意到我是如何在某些地方添加await的,并且它在我的8节点以上版本上都有效?

好的,我看到许多其他错误可能导致代码失败,例如您在promise函数中返回错误的方式以及许多其他小错误。但是,我希望这可以帮助您解决问题。

如有疑问,请记住阅读,搜索和询问

关注实际问题(即:具有基本知识,编写适当的代码等),而不是您认为的第一个问题(即:两个代码之间的差异)

希望这对您很满意。和平。