Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add promise support #62

Closed
monolithed opened this issue Jul 5, 2015 · 26 comments · Fixed by #339
Closed

Add promise support #62

monolithed opened this issue Jul 5, 2015 · 26 comments · Fixed by #339

Comments

@monolithed
Copy link

import Xray from 'x-ray';

let scraper = Xray();

scraper(url, {
    items: scraper('.scope', [
        {
            link: '.link@href'
        }
    ])
})
.then(
     result => {
        // All done
     },
     error => {
        // There're errors
     }
});
@monolithed monolithed changed the title Promise support Add promise support Jul 5, 2015
@matthewmueller
Copy link
Owner

yah +1, this should be pretty easy to add

@kengz
Copy link
Contributor

kengz commented Jul 10, 2015

I made an attempt while wrapping x-ray for my mini-library, this might help if you can't wait for the new release:

function scrape(url, scope, selector) {
    var defer = q.defer();
    x(url, scope, selector)(function(err, res){
        defer.resolve(res);
    })
    return defer.promise;
}

Of course by no means it's complete. I didn't need the err so I ignored it.

@hutber
Copy link

hutber commented Jul 15, 2015

+1 to this

1 similar comment
@hutber
Copy link

hutber commented Jul 15, 2015

+1 to this

@l4u
Copy link

l4u commented Jul 28, 2015

With bluebird,

var getTitle = Promise.promisify(x('http://google.com', 'title'));

Full example

var Promise = require('bluebird');
var Xray = require('x-ray');
var x = Xray();
var getTitle = Promise.promisify(x('http://google.com', 'title'));
getTitle().then(function(title) {
    console.log(title);  // Google
});

@yang-wei
Copy link

yang-wei commented Sep 8, 2015

is anyone implementing this?

@kengz
Copy link
Contributor

kengz commented Sep 8, 2015

@yang-wei Bluebird is the way to go - the most elegant and easiest solution. @monolithed do you wanna mark this as resolved?

@monolithed
Copy link
Author

@kengz, I use a simple wrapper like:

//  xray.js
import Xray from 'x-ray';

export default class {
    get (url, data) {
        return Promise((resolve, reject) => {
            let xray = Xray();

            xray(url, data)((error, result) => {
                if (error) {
                    reject(error);
                }
                else {
                    resolve(result);
                }
            });
        });
    }
};

//  usage.js
import Xray from './xray';

let xray = new Xray;

xray.get(url, data).then(result => { });

But, the issue is not resolved yet.

@itzjonas
Copy link

itzjonas commented Nov 5, 2015

+1

1 similar comment
@marciovicente
Copy link

👍

@kengz
Copy link
Contributor

kengz commented Jan 12, 2016

Actually made a package months ago for this by extending my answer above. Just putting this out there:
reqscraper

@monolithed
Copy link
Author

@kengz, I'm sorry, but you package does not relate to the current issue.

@tomalex0
Copy link

In current setup, can we use promise without adding bluebird?

I'm looking to get image dimension using https://github.com/nodeca/probe-image-size
how can i chain it using x-ray and promise

        var url  =  'https://www.npmjs.com/';
        x(url, {
            page_title : 'title',
            'page_images' : x('img', [{
                src: '@src',
                height: '@height',
                width: '@width'
            }]),
            metatags_name: x('meta', [{
                name: '@name',
                description: '@content'
            }]),
            metatags_property: x('meta', [{
                name: '@property',
                description: '@content'
            }])
        })(function (err, obj) {             
            res.json(obj);
        });

@obsidience
Copy link

To add to monolithed's answer, I'm using the following:

"use strict";

var xray = (require('x-ray'))();

module.exports.Xray = class {
    nestedGet (scope, selector) {
            return xray(scope, selector);
    };
    get (url, scope, selector) {
        return new Promise((resolve, reject) => {
            xray(url, scope, selector)((error, result) => {
                if (error) {
                    debugger;
                    reject(error);
                }
                else {
                    resolve(result);
                }
            });
        });
    };
};

Usage:

"use strict";

var x = new (require('./xray.js')).Xray()
x.get("http://domain.com", "table tr[valign='middle']", [{dateAndTime: 'td:nth-child(1)', links: x.nestedGet('a', [{name: '', link: '@href'}])}])
    .then((result) => {
        debugger;
    });

I'm not a guru, so feedback welcomed.

@Kikobeats
Copy link
Contributor

Code example looks goods, but I think that promise is not a feature for this library.

You can use pify out of the box. It converts callback style into Promise style.

@monolithed
Copy link
Author

monolithed commented Mar 22, 2016

Promise is not a feature. It should be part of any library ))

@Kikobeats
Copy link
Contributor

Very related with caolan/async#956.

@monolithed ideally yes, but I feel that first we need to put effort have a better codebase for do it. I'm trying to uncouple some methods that are not related with x-ray interface. Later, we can a promise wrapping around the API methods

@Kikobeats Kikobeats added the api label Mar 22, 2016
@ivawzh
Copy link

ivawzh commented Apr 14, 2016

I made a util for xray-to-promise:

export function xToPromise(xQuery) {
  return new Promise((resolve, reject) => {
    xQuery((err, results) => {
      if (err) {
        reject(err)
      } else {
        resolve(results)
      }
    })
  })
}

const query = x(page, 'title')
xToPromise(query).then(
  data => {
    console.log(data)
  },
  err => {
    console.log(err)
  }
)

@ronenteva
Copy link

+1

@matthewmueller
Copy link
Owner

^ you just emailed 13 people. please use the thumbs up button instead

@kengz
Copy link
Contributor

kengz commented Dec 26, 2016

New solution that works much better with xray's chainable functions:

Note that external dependencies are used, so you need to do npm install --save bluebird lodash stream-to-string

const Promise = require('bluebird')
const _ = require('lodash')
const sts = require('stream-to-string')
const Xray = require('x-ray')


function streamToPromise(stream) {
  return new Promise((resolve, reject) => {
    sts(stream, (err, resStr) => {
      if (err) {
        reject(err)
      } else {
        resolve(JSON.parse(resStr))
      }
    })
  })
}

// inject a promisify function to xray using its stream()
// called xray().promisify() to return a promise
function injectWithPromisify(xray) {
  const wrapx = _.wrap(xray, (fn, urlStr, scope, selector) => {
    const initx = fn(urlStr, scope, selector)
    const promisify = function pfn() {
      return streamToPromise(initx.stream())
    }
    _.assign(initx, { promisify })
    return initx
  })
  return wrapx
}

// return a new instance of xray with options
function newXray(){
  let xray = Xray()
  xray = injectWithPromisify(xray)
  return xray
}

// Usage
const xray = newXray()
const url = 'http://www.imdb.com/'
const selector = ['title']

xray(url, selector).promisify()
  .then((res) => {
    console.log(JSON.stringify(res))
  }).catch((err) => {
    console.log(err)
  })

What it does is adding a promisify function to the xray(...) instance, while preserving the others. When executed, the promisify() will internally call xray().stream() and pipe that to a promise, which is returned.
Since this only injects a new promisify function to the original xray() instance, one can chain functions as usual before calling promisify():

xray(url, selector)
  .paginate('.next_page@href')
  .limit(3)
  .promisify()
  .then((res) => {
    console.log(JSON.stringify(res))
  }).catch((err) => {
    console.log(err)
  })

@matthewmueller
Copy link
Owner

nice! fwiw, I'd totally accept a PR that does something like this:

Xray.prototype.then = function () {
  return new Promise(function (resolve, reject) {
     // ...
  })
}

@kengz kengz mentioned this issue Dec 29, 2016
5 tasks
@kengz
Copy link
Contributor

kengz commented Dec 29, 2016

@matthewmueller yep, turns out to be way cleaner if done from within the source code. Please review, and advice on some of the unchecked items in the PR

@viniciusCamargo
Copy link

viniciusCamargo commented May 12, 2017

Heys, guys!

Let me share what I've been doing for a while now, maybe it could help someone (it's similar to the solution by @monolithed).
Please, keep in mind that I'm not totally sure if it was the best approach for the task, nor if is performant or compliant to best practices. That said, I must say that it's been working really well for me since I haven't seen any issues so far.

const Xray = require('x-ray')

const request = (params) => {
  const { url, filters, limit } = params
  const { parent, children, pagination } = params.selectors

  const x = filters ? Xray({ filters }) : Xray()

  return new Promise((resolve, reject) => {
    try {
      x(url, parent, children)
      ((error, results) => {
        if (error) reject(error)

        resolve(results)
      }).paginate(pagination).limit(limit)
    } catch (error) {
      reject(error)   
    }
  })
}

module.exports = request

Then I'd just pass an object like this:

const obj = {
  url,
  filters,
  selectors: {
    parent,
    children,
    pagination
  },
  limit
}

request(obj)
  .then(console.log)

Let me know if you'd have any idea on how to improve it. :)

@0xgeert
Copy link
Contributor

0xgeert commented May 14, 2017 via email

@gnesher
Copy link

gnesher commented Jan 29, 2018

Hi guys,
Is there still an active plan of adding promise support for the queue / priorityQueue? This has been open for a good while

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.