【问题标题】:Callback & Async problems with Node & LambdaNode 和 Lambda 的回调和异步问题
【发布时间】:2018-09-17 18:36:28
【问题描述】:

我正在为 Alexa 智能家居技能创建一个 lambda 函数来控制我的接收器。我正在使用亚马逊提供的样板示例代码,但我是 node 新手,在等待我的回调设置响应时遇到了很多麻烦。我一直在尝试操作,好像它们的一般结构在这里是正确的。

我可以调用它,它可以很好地执行 POST 请求,但它总是返回 null(Alexa 不喜欢)。它也没有在 response.on('end' 循环中记录任何内容。似乎它正在通过 marantzAPI 调用进行爆炸。我尝试在 marantzAPI 函数(命令,回调)中使用回调,最终得到相同的结果. 我觉得有些东西我只是不明白。

这是 lambda 函数:

'use strict';

var http = require('http');
var qs = require('querystring');

/**
 * We're not discovering our devices, we're just hardcoding them.  Easy!
 */
const USER_DEVICES = [
    {
        applianceId: 'marantz-sr6010-shield',
        manufacturerName: 'Marantz nVidia',
        modelName: 'SR6010 Shield',
        version: '1.0',
        friendlyName: 'Shield',
        friendlyDescription: 'nVidia Shield via Marantz SR6010',
        isReachable: true,
        actions: ['turnOn', 'turnOff'],
    }
];

/**
 * Utility functions
 */

function log(title, msg) {
    console.log(`[${title}] ${msg}`);
}

/**
 * Generate a unique message ID
 *
 * https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
 * This isn't UUID V4 but it's good enough for what we're doing
 */
function generateMessageID() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

/**
 * Generate a response message
 *
 * @param {string} name - Directive name
 * @param {Object} payload - Any special payload required for the response
 * @returns {Object} Response object
 */
function generateResponse(name, payload) {
    return {
        header: {
            messageId: generateMessageID(),
            name: name,
            namespace: 'Alexa.ConnectedHome.Control',
            payloadVersion: '2',
        },
        payload: payload,
    };
}

/**
 * This is a lot easier when I'm just hard-coding my devices
 */
function getDevicesFromPartnerCloud() {
    return USER_DEVICES;
}


/**
 * The meat and potatoes, I'm butchering just the things I Need from the solid work done by Nathan Totten:
 * https://github.com/ntotten/marantz-avr/blob/master/lib/avreciever.js
 */

function marantzAPI(commands, apiCallback) {
    log('DEBUG', `MarantzAPI Invoked: ` + JSON.stringify(commands));
    var postData = {};

    // format commands for the Marantz POST (cmd0: cmd1: etc)
    // note: may need to send commands one at a time??
    for (var i=0; i<commands.length; i++) {
        postData['cmd' + i] = commands[i];
    }

    log('DEBUG', `MarantzAPI POST Data: ` + qs.stringify(postData));

    var serverError = function (e) {
        log('Error', e.message);
        apiCallback(generateResponse('UnexpectedInformationReceivedError', e.message));
    };    

    var httpCallback = function(response) {
        response.on('end', function () {
            log('DEBUG', `API Request Complete`);
            apiCallback(generateResponse('APIRequestComplete', postData));
        });        

        response.on('error', serverError); 
    };

    var apiRequest = http.request({
        hostname: process.env.receiverIp,
        path: '/MainZone/index.put.asp',
        port: process.env.receiverPort,
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(qs.stringify(postData))
        },
    }, httpCallback);  

    apiRequest.on('error', serverError);
    apiRequest.write(qs.stringify(postData));
    apiRequest.end();    
} 


/**
 * Main logic
 */

function handleDiscovery(request, callback) {
    log('DEBUG', `Discovery Request: ${JSON.stringify(request)}`);

    const userAccessToken = request.payload.accessToken.trim();

    const response = {
        header: {
            messageId: generateMessageID(),
            name: 'DiscoverAppliancesResponse',
            namespace: 'Alexa.ConnectedHome.Discovery',
            payloadVersion: '2',
        },
        payload: {
            discoveredAppliances: getDevicesFromPartnerCloud(userAccessToken),
        },
    };

    log('DEBUG', `Discovery Response: ${JSON.stringify(response)}`);

    callback(null, response);
}

function handleControl(request, callback) {
    log('DEBUG', `Control Request: ${JSON.stringify(request)}`);

    const userAccessToken = request.payload.accessToken.trim();
    const applianceId = request.payload.appliance.applianceId;

    let response;
    var commands = [];

    switch (request.header.name) {
        case 'TurnOnRequest':
            // turn on the device
            commands.push('PutZone_OnOff/ON');

            // set the input
            switch (applianceId) {
                case 'marantz-sr6010-shield':
                    commands.push('PutZone_InputFunction/MPLAY');
                    break;
            }

            // I guess?  Not even sure if it actually does all this.
            commands.push('aspMainZone_WebUpdateStatus/');

            marantzAPI(commands, function(response) {
                callback(null, response);
            });
            break;

        default: {
            log('ERROR', `No supported directive name: ${request.header.name}`);
            callback(null, generateResponse('UnsupportedOperationError', {}));
            return;
        }
    }

    // I think I need to remove these, because response is not set at execution time
    // log('DEBUG', `Control Confirmation: ${JSON.stringify(response)}`);
    // callback(null, response);
}

exports.handler = (request, context, callback) => {

    switch (request.header.namespace) {
        case 'Alexa.ConnectedHome.Discovery':
            handleDiscovery(request, callback);
            break;

        case 'Alexa.ConnectedHome.Control':
            handleControl(request, callback);
            break;

        default: {
            const errorMessage = `No supported namespace: ${request.header.namespace}`;
            log('ERROR', errorMessage);
            callback(new Error(errorMessage));
        }
    }
};

【问题讨论】:

  • 那是相当多的代码......问题在哪里发生?乍一看,回调用法似乎是正确的,这个问题将受益于拥有 MCVE
  • 我已经清理过了——这主要是来自亚马逊智能家居技能的样板,但我已经将它包含在上下文中。我认为问题在于handleControl 事件正在调用我的marantzAPI 函数,但脚本只是在终止。 marantzAPI HTTP 回调中的 response.on('end' 循环中没有任何内容被注销。

标签: node.js aws-lambda alexa alexa-skills-kit


【解决方案1】:

Node js 环境默认为异步环境,您的 function marantzAPI(commands) 方法将异步执行,因此在执行此函数时,控件不会停在那里并继续执行下一个函数并最终执行 apiRequest.end(); 而之前的功能尚未完成导致空白响应。这是最可能的答案,我建议您访问 This SO thread 以获取详细说明并可能找到解决方案。

【讨论】:

  • 我已经看到了那个答案,我曾一度尝试让我的 marantz 函数正确实现回调,但它并没有改变返回的结果。我已经更新了上面的节点函数来展示我的尝试。我认为在回调方面我仍然缺少一些基本的东西......
【解决方案2】:

我已经开始工作了。这是我更新的 marantzAPI 函数。看起来 response.on('data' 对 HTTP 请求的成功至关重要?不知道。

function marantzAPI(commands, apiCallback) {
    var postData = {};

    // format commands for the Marantz POST (cmd0: cmd1: etc)
    // note: may need to send commands one at a time??
    for (var i=0; i<commands.length; i++) {
        postData['cmd' + i] = commands[i];
    }

    log('DEBUG', `MarantzAPI Called w Data: ` + qs.stringify(postData));

    var serverError = function (e) {
        log('Error', e.message);
        apiCallback(false, e.message);
    };    

    var apiRequest = http.request({
        hostname: process.env.receiverIp,
        path: '/MainZone/index.put.asp',
        port: process.env.receiverPort,
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(qs.stringify(postData))
        },
    }, function(response) {

        response.setEncoding('utf8');
        response.on('data', function (chunk) {
            log('DEBUG', 'CHUNK RECEIVED');
        });

        response.on('end', function () {
            log('DEBUG', `API Request Complete`);
            apiCallback(true, '');
        });        

        response.on('error', serverError); 
    });

    apiRequest.on('error', serverError);
    apiRequest.write(qs.stringify(postData));
    apiRequest.end();    
} 

【讨论】:

    猜你喜欢
    • 2018-02-09
    • 1970-01-01
    • 2018-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 1970-01-01
    • 2015-08-04
    相关资源
    最近更新 更多