Problem with ParticleJS and Node asynchronicity

Already posted this on Slack, but here goes. I am learning Node and trying to use ParticleJS. When using the example code from https://docs.particle.io/reference/javascript/, it tries to list devices before the access token has been obtained. I’m still getting used to the asynchronicity of Node. :wink:

var Particle = require('particle-api-js');
var particle = new Particle();
var token;

particle.login({username: 'email', password:
'pass'}).then(
  function(data) {
    token = data.body.access_token;
    console.log('Got token!');
  },
  function (err) {
    console.log('Could not log in.', err);
  }
);


var devicesPr = particle.listDevices({ auth: token });

devicesPr.then(
  function(devices){
    console.log('Devices: ', devices);
  },
  function(err) {
    console.log('List devices call failed: ', err);
  }
);

Output:

List devices call failed:  { Error: HTTP error 400 from https://api.particle.io/v1/devices - The access token was not found
    at /Users/nrobinson/test/node_modules/particle-api-js/lib/Agent.js:184:19
    at Request.callback (/Users/nrobinson/test/node_modules/superagent/lib/node/index.js:631:3)
    at /Users/nrobinson/test/node_modules/superagent/lib/node/index.js:795:18
    at IncomingMessage.<anonymous> (/Users/nrobinson/test/node_modules/superagent/lib/node/parsers/json.js:16:7)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)
    at _combinedTickCallback (internal/process/next_tick.js:74:11)
    at process._tickCallback (internal/process/next_tick.js:98:9)
  statusCode: 400,
  errorDescription: 'HTTP error 400 from https://api.particle.io/v1/devices - The access token was not found',
  shortErrorDescription: 'The access token was not found',
  error:
   { Error: Bad Request
       at Request.callback (/Users/nrobinson/test/node_modules/superagent/lib/node/index.js:626:17)
       at /Users/nrobinson/test/node_modules/superagent/lib/node/index.js:795:18
       at IncomingMessage.<anonymous> (/Users/nrobinson/test/node_modules/superagent/lib/node/parsers/json.js:16:7)
       at emitNone (events.js:91:20)
       at IncomingMessage.emit (events.js:185:7)
       at endReadableNT (_stream_readable.js:974:12)
       at _combinedTickCallback (internal/process/next_tick.js:74:11)
       at process._tickCallback (internal/process/next_tick.js:98:9)
     original: null,
     response:
      Response {
        domain: null,
        _events: {},
        _eventsCount: 0,
        _maxListeners: undefined,
        res: [Object],
        request: [Object],
        req: [Object],
        links: {},
        text: '{"error":"invalid_request","error_description":"The access token was not found"}',
        body: [Object],
        files: undefined,
        buffered: true,
        headers: [Object],
        header: [Object],
        statusCode: 400,
        status: 400,
        statusType: 4,
        info: false,
        ok: false,
        redirect: false,
        clientError: true,
        serverError: false,
        error: [Object],
        accepted: false,
        noContent: false,
        badRequest: true,
        unauthorized: false,
        notAcceptable: false,
        forbidden: false,
        notFound: false,
        charset: 'utf-8',
        type: 'application/json',
        setEncoding: [Function: bound ],
        redirects: [] },
     status: 400 },
  body:
   { error: 'invalid_request',
     error_description: 'The access token was not found' } }
Got token!

You can make the device list call within the return from the login. Though that works, that will quickly have you ending up in a ‘callback hell’. Instead, try googling around a bit for promises. They’re a bit abstract at first, but make some more sense when you get to know them better, and they’re much more suitable for situations like these :slight_smile:

2 Likes

perhaps an easier way to look at Promises is to compartmentalize them into chain-able functions.

build individual functions that return a promise:

function myFirstEvent(){
  return new Promise((resolve, reject) => {
    //function here 
    if(error) reject(error);
    if(something) resolve(some args);
    else resolve(some args);
  )};
}

then chain your promises:

myFirstEvent()
.then(mySecondEvent)
.then(well, you get it)
.catch(function(reason) {
   // rejection and error handling
});
2 Likes

How would I make something like this with the code above?

I suppose you are not using OAuth, rather simply logging in with your account credentials… yes?

At the moment, yes. There also appears to be many syntax issues with myFirstEvent, according to JSHint:

function myFirstEvent(){
  return new Promise((resolve, reject) => {

    //function here 

    if(error) reject(error);
    if(something) resolve(args);
    else resolve(args);
  )};
}

so, although there is a JS api, we didn’t really use any of the JS wrapper, rather we simply used the RESTful methods using request.

Since Particle.login returns a promise and passes data and err.

so I would start with:

function loginToAccount(credentials) {
    return new Promise((resolve, reject) => {
        Particle.login(credentials).then(
            (data) => {
                resolve(data.body.access_token);
            },
            (error) => {
                reject(error);
            });
    });
}

so…

loginToAccount(credentials)
.then(devicesPr)
.then(function(devices){
    console.log('Devices: ', devices);
  },
  function(err) {
    console.log('List devices call failed: ', err);
  }
);

no promises that it will work!

excuse the pun!

4 Likes

@nrobinson2000,

here is the function tested OK:

var Particle = require('particle-api-js');
var particle = new Particle();

let credentials = { username: 'email@email.com', password:'password'};

function getAccessToken(credentials) {
  console.log("loginToAccount()");
  return new Promise((resolve, reject) => {
    particle.login(credentials).then(
      (data) => {
        console.log(`retrieved access token:${data.body.access_token}`);
        resolve(data.body.access_token);
      },
      (err) => {
        reject(err);
      });
    });
}

function getDeviceList(token) {
  console.log("getDeviceList()");
  return new Promise((resolve, reject) => {
    particle.listDevices({'auth':token}).then(
      (devices) => {
        let deviceList = [];
        devices.body.forEach(function(element) {
          deviceList.push(element.id);
        });
        console.log(`You have ${devices.body.length} devices: ${deviceList}`);
        resolve();
      },
      (err) => {
        reject(err);
      });
    });
}

getAccessToken(credentials)
.then(getDeviceList)
.catch(function(err){
  console.log(err);
});

3 Likes

This works! Thanks.

1 Like

NodeJS 7.6 (I think) and newer support async and await which make this mess a whole lot easier and much more clear.

So, hopefully most of us won’t have to deal with promises by themselves for much longer.

1 Like