首页 > 解决方案 > 等待 JavaScript 中的变量

问题描述

我正在使用nodeJS。我想等待对象内的属性变为真,然后继续执行代码。

这是我的代码的样子:


export async function createRun() {
    try {
        let shared = { url: "", device: "", finished: new Promise(() => {}) };

        const browser = await launch();

        const page = await browser.newPage();

        await page.setDefaultNavigationTimeout(0);

        await page.setBypassCSP(true);

        await page.exposeFunction(
            "onMessageReceivedEvent",
            async (e: { type: string; data: Message }) => {
                if (e.data === "finished") {
                    shared.finished = ;
                }
            }
        );

        const listenFor = (type: string) => {
            return page.evaluateOnNewDocument((type: any) => {
                window.addEventListener(type, (e) => {
                    // @ts-ignore
                    window.onMessageReceivedEvent({ type, data: e.data });
                });
            }, type);
        };

        await listenFor("message");

       console.log('before');
       // wait for shared.finished to become true
       // continue execution
       console.log('after')
}

如何实施?提前致谢!

标签: javascriptnode.js

解决方案


好的,现在有了所有详细信息,可以回答您的问题。

得到结果认为暴露的函数可能 - 乍一看 - 会阻止 Promise 的使用。但是您可以为此使用闭包。

因此,您在 a 中创建回调函数new Promise,并将其分配给该变量之外的变量。在您的回调中,您可以在满足resolve您的条件时做出承诺e.data === "finished"

        let onMessageReceivedEventCallback;
        const messageReceived = new Promise((resolve, reject) => {
          onMessageReceivedEventCallback = (e: { type: string; data: Message }) => {
              if (e.data === "finished") {
                  resolve();
              }
              // TODO you might want to reject in case an error occurs here, so that your application won't halt
          }
          // TODO if there is no specific error case then you might reject here after a given timeout
          // setTimout(() => reject(new Error("Timeout")), 1000);
        })

然后你将该函数传递给你的page.exposeFunction,然后你await messageReceived用来等待该承诺得到解决。

export async function createRun() {
    try {
        let shared = { url: "", device: "" };

        const browser = await launch();

        const page = await browser.newPage();

        await page.setDefaultNavigationTimeout(0);

        await page.setBypassCSP(true);

        
        let onMessageReceivedEventCallback;
        const messageReceived = new Promise((resolve, reject) => {
          onMessageReceivedEventCallback = (e: { type: string; data: Message }) => {
              if (e.data === "finished") {
                  resolve();
              }
              // TODO you might want to reject in case an error occurs here, so that your application won't halt
          }
          // TODO if there is no specific error case then you might reject here after a given timeout
          // setTimout(() => reject(new Error("Timeout")), 1000);
        })


        await page.exposeFunction(
            "onMessageReceivedEvent", onMessageReceivedEventCallback
        );

        const listenFor = (type: string) => {
            return page.evaluateOnNewDocument((type: any) => {
                window.addEventListener(type, (e) => {
                    // @ts-ignore
                    window.onMessageReceivedEvent({ type, data: e.data });
                });
            }, type);
        };

        await listenFor("message");

       console.log('before');
       await messageReceived;
       console.log('after')
}

要回答您最初的问题,技术上可以使用 getter、setter 或 Proxy 来实现类似的目标。在下文中,我将展示如何使用代理来完成类似的操作。但请注意,我强烈反对使用它。它只是掩盖了代码中发生的事情,我无法想象一个真正有意义的用例。

function createWaitAbleProperties(initialObject = {}, timeout = 2000) {

  function initProperty(obj, name) {
    obj.properties[name] = {}
    
    // utelizing a defere pattern which is not recommended
    // it is not as bas as the regular one due to the timeout but it is still bad
    obj.properties[name].promise = new Promise((resolve, reject) => {
      obj.properties[name].resolve = resolve
      setTimeout(() => reject(new Error('timeout for ' + name)), timeout);
    })
  }

  return new Proxy(initialObject, {
    properties: {},
    get: function(obj, prop) {
      let match;
      if (match = prop.match(/^(.*)Promise$/)) {
        if (!this.properties[match[1]]) {
          initProperty(this, match[1])
        }
        return this.properties[match[1]].promise
      } else {
        return this.properties[prop]?.value
      }
    },
    set: function(obj, prop, value) {
      if (!this.properties[prop]) {
        initProperty(this, prop)
      }
      this.properties[prop].resolve()
      this.properties[prop].value = value
    }
  });
}


async function run() {
  const observer = createWaitAbleProperties()
  
  observer.value2 = 200

  setTimeout(() => {
    observer.value1 = 100
  }, 1000)

  await observer.value1Promise
  await observer.value2Promise
  console.log('finished', observer.value1, observer.value2)
}

run()


推荐阅读