【问题标题】:variable not assigning inside promise变量未在承诺内分配
【发布时间】:2018-12-23 16:21:29
【问题描述】:

也许我困了,但是在什么情况下会出现以下情况?

let foo;

page
  .evaluate(() => {
    // return works... but not closure assignment

    // doesn't work
    foo = 'foo';

    // works
    return 'bar';
  })
  .then(bar => {
    console.log('foobar', foo, bar);
    // > foobar undefined bar
  });

这发生在使用 puppeteer 的 mocha 测试中

更新:完整的代码

节点 9.11.2

/* global describe, it, before, after */

const fs = require('fs-extra');
const path = require('path');
const assert = require('assert');
const puppeteer = require('puppeteer');
const sleep = require('shleep');

const extPath = path.resolve(__dirname, '..', 'build');
const { name } = fs.readJSONSync(path.resolve(extPath, 'manifest.json'));

// Access chrome object in Extensions
// https://github.com/GoogleChrome/puppeteer/issues/2878

describe('chrome extension', () => {
  let browser;
  let extensionPage;

  before(async function() {
    this.timeout(90 * 1000);

    // start puppeteer
    browser = await puppeteer.launch({
      headless: false,
      args: [
        `--disable-extensions-except=${extPath}`,
        `--load-extension=${extPath}`
      ]
    });

    // poll instead of hope this is enough time?
    const EXT_LOAD_DELAY = 100;
    await sleep(EXT_LOAD_DELAY);

    const targets = await browser.targets();

    const extensionTarget = targets.find(
      ({ _targetInfo }) =>
        _targetInfo.type === 'background_page' && _targetInfo.title === name
    );

    const page = await extensionTarget.page();

    let foo;

    page
      .evaluate(() => {
        // return works... but not closure assignment

        // doesn't work
        foo = 'foo';

        // doesn't log
        console.log('foo', foo);

        // works
        return 'bar';
      })
      .then(bar => {
        console.log('foobar', foo, bar);
        // > foobar undefined bar
      });
  });

  it('should load', async () => {
    assert(true);
  });
});

测试截图

【问题讨论】:

  • 如果evaluate的回调在then的回调被调用之前被调用,上面的方法肯定会起作用。您是否检查过以确保evaluate 的合同规定会发生这种情况?编辑:Looks to me like it does。上面的代码肯定会分配给顶部声明的foo。您遇到问题的真实代码必须不同(例如,您是否在页面函数中声明了foo?)。
  • 由于您没有使用async 函数,因此async-await 标记无关紧要。
  • page.evaluate 是否在不同的上下文中运行代码?就像在单独的浏览器中一样,具有完全不同的变量?
  • 好吧,它可以在函数上调用.toSource(),将字符串发送到浏览器,然后eval它。
  • ...是的,这正是它的作用:github.com/GoogleChrome/puppeteer/blob/master/lib/…

标签: javascript promise async-await mocha.js puppeteer


【解决方案1】:

puppeteer 中的evaluate 方法没有您在代码中本地或全局声明的变量的概念。您传递给evaluate 方法的函数是要在页面上下文(即在您的浏览器中)执行的函数。由于foo 未在页面上下文中声明,因此无法访问它,因此无法更新其值。

所以要逐步执行您的代码:

let foo;

await page.evaluate(() => {
  foo = 'foo';  // Since no variable foo is known to the evaluate method in the context of your page
                // this line , effectively, assigns a new variable called foo with a value 'foo' which
                // is then lost once the method has completed since its value is never returned.

  return 'bar'; // This value is returned from the method and is therefore retained below
})
.then(bar => {
  console.log('foobar', foo, bar);
  // foobar is output as expected since you've hardcoded it

  // foo is now referring to the global foo you declared earlier but since you have used `let` and not
  // assigned it any value, it is output as 'undefined'

  // bar is the value you returned from your 'evaluate' function and is therefore output as 'bar' as
  // expected in the console log.
});

如果您想使用 evaluate 更新您的变量 foo,您必须这样做:

let foo;
foo = await page.evaluate(() => {
  return 'foo'
});
console.log(foo); // Now outputs 'foo' as expected

但是,您可以将变量注入evaluate 方法并更新它们的值(如果您愿意),例如:

let foo = 'foo'
console.log(foo); // Outputs 'foo' as expected
foo = await page.evaluate((injectedFoo) => {
  return `new${injectedFoo}`;
}, foo);
console.log(foo); // Outputs 'newfoo'

所以这里发生的情况是,您已将变量 foo 注入到 evaluate 方法中,方法是在方法声明的末尾将其作为参数传递。 evaluate 方法现在包含一个变量(为了清楚起见,我将其称为 injectedFoo),它带有 foo 变量的原始值。

然后,我将附加到 foo 变量字符串开头的字符串 new 返回并在控制台中输出该字符串的最终值。

我希望这有助于解释evaluate 方法的工作原理!

【讨论】:

    猜你喜欢
    • 2018-08-30
    • 2020-04-30
    • 2019-09-18
    • 2020-05-10
    • 1970-01-01
    • 2017-11-28
    • 2020-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多