Core: Issue with yielding in callbacks

Created on 16 Oct 2016  路  23Comments  路  Source: adonisjs/core

Hello, I'm begginer in nodejs and especially generators app so i don't know how to do this on my own. In Google there is a lack of information:

twitch.getAuthenticatedUserChannel(body.access_token, collectInformation);

function collectInformation(err, body) { if(err) { console.dir(err); } else { HomeController.prototype._user = { twitch_id: body._id, username: body.name, display_name: body.display_name, logo: body.logo }; } }

I want to yield information about user to session but i can't do this in collectInformation because there is an error of can't use yield in function (i think so).

Second question is: How can bind other HomeController function to callback for ex.
* logged(request, response) { [...] twitch.getAuthenticatedUserChannel(body.access_token, collectInformation); //instead of collectInfromation() how can i bind finish() presented below?
* finish() { console.log(HomeController.prototype._user); }

All 23 comments

Hi @Perfect7M

I add the sample problem as you, the solution here is the promises (they can be yielded).

You should try to transform your method to a Promise, for this, I use https://github.com/digitaldesignlabs/es6-promisify and it works well for me:

const promisify = require('es6-promisify');
cont getAuthenticatedUserChannel = promisify(twitch.getAuthenticatedUserChannel);
// So now you can use yield
const res = yield getAuthenticatedUserChannel(body.access_token)
// Here you can use res.name, res._id, etc...

I'm not sure it's a good idea to set: HomeController.prototype._user = { ... }

You should use the context directly: this._user = { ... }
And them you can access it directly inside your controller.

I hope it helps you to understand the generators a bit better 馃槃

Yes i tried to use Promise but when i get error message that 'callbacks can't be made promise'.
Can't do this like above because this plugin https://www.npmjs.com/package/twitch-api make use of callbacks and you must provide callback for every use of its function but callbacks can't be promise and there is a problem.

Thanks for example, I use it for sure :)

Can you try my example and tell me if it's working?

es6-promisify module will handle the callback for you don't worry

Cannot read property 'clientId' of undefined

        const twitch = new TwitchAPI({
            clientId: Config.get('twitch.client_id'),
            clientSecret: Config.get('twitch.client_secret'),
            redirectUri: Config.get('twitch.redirect_uri'),
            scopes: Config.get('twitch.scopes')
        });

        const code = request.get().code;

        console.log(twitch);

        const getToken = Promisify(twitch.getAccessToken);
        const res = yield getToken(code);

        console.log(res);

First i must obtain access_token co i must send code from get... but its not working. When i do console.log(twitch); evrything is setted up.

I found the bug @Perfect7M

It's because twitch-api use an instance and the getAccessToken method is from the prototype, so you just have to bind the twitch.getAccessToken:

const getToken = Promisify(twitch.getAccessToken.bind(twitch));

Voil脿 馃槃

Great it worked 馃槃 I am closing the issue, please feel free to report any issues/bugs

Hello @Atinux, @Perfect7M
I am using this npm package https://www.npmjs.com/package/node-twitter-api
from your above reference used es6-pomisify with following code

const getToken = promisify(twitter.getRequestToken.bind(twitter), {multiArgs: true})

getToken().then(function (result) {
   console.log(result) // Is valid checked ok
   yield request.session.put({'requestToken' : result[0], 'requestTokenSecret' : result[1]})
});

Again got error at yield ing request.session

Please guide next.
Thanks

@navdeepsingh

const getToken = promisify(twitter.getRequestToken.bind(twitter), {multiArgs: true});
const result = yield getToken();
 yield request.session.put({'requestToken' : result[0], 'requestTokenSecret' : result[1]})

These should work for u..
But i would reccomend u to take classes for generators..

@muxahuk

I tried.. but got following err

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)

please explain about classes for generators to use.
thks!!

Can you provide us you code example?

@navdeepsingh

These error is related to you'r api, not to these code particular ( if it works with promise - it should work with yield )

these code should be same as:

return getToken().then(function (result) {
   console.log(result) // Is valid checked ok
   return request.session.put({'requestToken' : result[0], 'requestTokenSecret' : result[1]})
}).then( function() {
  console.log( 'session should be set here' );
  return response.send( 'something' );
} );

If these code doesn't works than u have problem not with yield..

@muxahuk

const getToken = promisify(twitter.getRequestToken.bind(twitter), {multiArgs: true})

    return getToken().then(function (result) {
      return {'requestToken' : result[0], 'requestTokenSecret' : result[1]}
    }).then( function(tokens) {
      console.log( tokens )
      yield request.session.put( tokens )
      return response.send( 'https://twitter.com/oauth/authenticate?oauth_token=' + tokens.requestToken );
    } );

Final code. But still yield of storing values in session not works. :(

@navdeepsingh

const getToken = promisify(twitter.getRequestToken.bind(twitter), {multiArgs: true})
const tokens = yield (getToken()
    .then(function(result) {
        return {
            'requestToken' : result[0],
            'requestTokenSecret' : result[1]
        }
    }))
yield request.session.put(tokens)
response.send('https://twitter.com/oauth/authenticate?oauth_token=' + tokens.requestToken)

Try this

Or this:

const getToken = promisify(twitter.getRequestToken.bind(twitter), {multiArgs: true})

const tokens =  yield getToken()
yield request.session.put({ requestToken : tokens[0], requestTokenSecret : tokens[1] })
return response.send( 'https://twitter.com/oauth/authenticate?oauth_token=' + request.session.requestToken )

@navdeepsingh

you still don't get it.. u CAN'T write yield in function(){} only in function *(){} ( generator ).

the difference between promise and generator is that in promise u have to chain you'r async calls with .then, but in generator u have to write yield before async function..
so if we have async function test

// promise:
return test().then( result => {
// do something with result
} );

// generator:
const result = yield test();
// do something with result

//so, for multiple calls it would be like so:

// primise:
return test().then( result => {
//do something with result
return test2(); // return enother async call
}).then( result2 => {
// do something with result2 witch is beeing return from test2 function
} );

generators:
const result = yield test();
// do something with result
const result2 = yield test2();
// do something with result2

So you'r mistake is writing yield inside promise

.then( function(tokens) {
      console.log( tokens )
      **yield request.session.put( tokens )**
      return response.send( 'https://twitter.com/oauth/authenticate?oauth_token=' + tokens.requestToken );
    } );

u need to return as with test2 function request.session.put function
so these should work:

const getToken = promisify(twitter.getRequestToken.bind(twitter), {multiArgs: true});
const sessionPut = promisify( request.session.put.bind( request.session ) );

    return getToken().then(function (result) {
      return {'requestToken' : result[0], 'requestTokenSecret' : result[1]}
    }).then( function(tokens) {
      console.log( tokens )
      return sessionPut( tokens )
      .then( () => tokens );
    } )
.then( function( tokens ) {return response.send( 'https://twitter.com/oauth/authenticate?oauth_token=' + tokens.requestToken ); } ) ;

@Atinux It prompts Error: Cant set headers after they are sent.

@muxahuk thanks for explaining in simple words to me.. I got overview now.
but promisifying request.session.put not seems work as it stucks at

 return sessionPut( tokens )
      .then( () => tokens );

I tried to work on this issue, yet not success.

@navdeepsingh try addong .catch at the end of the line to catch any error occured during promisses..

@muxahuk You cannot promisify the request.session.put method (see https://github.com/adonisjs/adonis-framework/blob/develop/src/Session/index.js#L212)

@navdeepsingh do you mind sharing the code of you Adonis controller? You might call response.send somewhere else before the promise is resolved.

@muxahuk
tried that too
https://gist.github.com/navdeepsingh/46fe7061cd5e9a7eb1d243f9a07892d6
but no response still.

@navdeepsingh I would suggest you to understand ES2015 generators.

TIP: You can simply yield promises instead of chaining then. Below should work

const getToken = promisify(twitter.getRequestToken.bind( twitter ), {multiArgs: true})

const result = yield getToken()
yield request.session.put('requestToken', result[0])
yield request.session.put('requestTokenSecret', result[1])
response.send(`https://twitter.com/oauth/authenticate?oauth_token=${tokens.requestToken}`)

@thetutlage i will continue my study for ES2015 features.

I tried above code of yield promises, again the same error of
Error: Can't set headers after they are sent.

Here is my code
https://gist.github.com/navdeepsingh/e57893a4fe5f26016fef86344bffe78c

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings