在承诺链中等待 DOM 元素中的突变

Wait in a promise chain for a mutation in a DOM element

我正在 Node.js 中制作某种网络抓取工具,它会拍摄出现在带有 PhantomJS.

的网站上的地图照片

但是,一旦打开页面,loading 消息就会出现在地图应该出现的位置。地图准备就绪后,消息消失 (visibility: hidden) 并显示地图。

因此,在 #loaderhidden 之前,我无法调用 page.render()(否则我会得到加载消息的图片,不太酷)。

// ... Open the page

.then(function(content) {
  return page.evaluate(function() {
    // Wait for #loading to get hidden somehow ...
    var clipRect = document.getElementById('map').getBoundingClientRect();
    return {
      top: clipRect.top,
      left: clipRect.left,
      width: clipRect.width,
      height: clipRect.height
    };
  });
})

// Render and process the picture ...

我考虑过使用 mutation observer,但找不到使用它的方法,因为我在 承诺链 中并且事件侦听器不会'不能如我所愿

我也尝试经常检查 visibility 属性,直到它被隐藏,正如 here 所解释的,但是 PhantomJS 通过 Node 的控制台报告:

TypeError: null is not an object (evaluating 'child.transform')

此外,我想尽可能避免这种变通方法,因为它们非常 CPU 密集。

关于如何在这种情况下等待 #loader 获得 hidden 有什么想法吗?

感谢phantomjs-node's mantainer, amir20, so all credit to him. As he explains in this issue我终于解决了这个问题:

waitFor expects to return a value. But evaluate returns a Promise. So that's why it is not working. This is not a problem of the module but rather problem with waitFor. Since everything is executed asynchronously then you have to wait for the value.

有问题的函数(由他创建)如下:

function waitUntil(asyncTest) {
    return new Promise(function(resolve, reject) {
        function wait() {
            asyncTest().then(function(value) {
                if (value === true) {
                    resolve();
                } else {
                    setTimeout(wait, 100);
                }
            }).catch(function(e) {
                console.log('Error found. Rejecting.', e);
                reject();
            });
        }
        wait();
    });
}

因此,应用到我的具体例子中,应该这样使用:

waitUtil(function() {
    return sitepage.evaluate(function() {
        return document.querySelectorAll('#loader').style.visibility == "hidden";
    })
}).then(function(){  // #loading is now hidden
    return page.evaluate(function() {
        var clipRect = document.getElementById('map').getBoundingClientRect();
        return {
            top: clipRect.top,
            left: clipRect.left,
            width: clipRect.width,
            height: clipRect.height
        };
    });
})

// Render and process the picture ...