以下javascript代码是否有未定义的行为?

时间:2017-02-09 10:38:42

标签: javascript ecmascript-6 undefined-behavior es6-promise



var resolve;

var head = { next: new Promise( r => resolve = r ) };

function addData(d) {
    resolve({
       data: d,
       next: new Promise(r => resolve = r)
    });
}




我编写了上面的代码来实现与linked-list类似的东西,而列表中的数据是异步加载的。

这个"链接列表"的负责人是head。列表中的每个节点都有两个字段.data.next,就像普通的链接列表一样。 .next是一个承诺,它将解析为列表中的下一个节点。

每次调用addData(...)时,列表中当前最后一个节点的.next字段将解析为新节点,从而成为新的最后一个节点。

我已在Node.js中验证了上述代码的功能,并且它按预期工作。以下是我用来验证行为的代码:



var resolve;
var head = { next: new Promise( r => resolve = r ) };
function addData(d) { resolve({ data: d, next: new Promise(r => resolve = r) }); }

async function verify() {
    while(true) {
        head = await head.next;
        console.log(head.data);
    }
}

verify();
addData(1); // outputs: 1
addData(2); // outputs: 2
addData(3); // outputs: 3




但是,我不确定此结构是否存在任何潜在问题(内存,效率)。另外,我特别担心这一行:

resolve({data: d, next: new Promise(r => resolve = r})

同时调用和分配resolve。哪个应该首先发生,分配或功能名称解析?这是一种未定义的行为吗?

谢谢!

2 个答案:

答案 0 :(得分:0)

  

首先应该发生哪种情况,分配或功能名称解析?

函数名称解析在评估参数之前发生(对象文字,包括Promise构造函数调用,后者又调用回调,其中包括赋值)。

  

这是一种未定义的行为吗?

没有。但代码闻起来。

  

有更好/更优雅的方法吗?

是。它主要取决于数据结构的生成方式。如果您使用的是函数式方法,我建议使用递归函数:

function getStream(i) {
    return new Promise(resolve => {
        setTimeout(resolve, 100);
    }).then(() => ({
        data: i,
        next: getStream(i+1)
    }));
}
(async function() {
    for (var data, next, head = getStream(1); {data, next} = await head; head = next) {
        console.log(data);
    }
}());

如果您使用其他人生成数据的命令式方法,我会将列表称为异步队列并放入适当的结构,但您的addData代码很好(除了你可以使用额外变量管理的resolve混淆之外。

答案 1 :(得分:0)

正如Bergi所指出的,这不是未定义的行为,但有一些方法可以实现这一点并不会让人感到困惑。

例如,您可以编写addData函数,以便它不会分配resolve函数并在同一语句中调用它:

function addData(d) { 
    let newResolve;

    resolve({ data: d, next: new Promise(r => newResolve = r) }); 

    resolve = newResolve;
}

看起来您可能正在使用非常迂回的方法来创建流或可观察集合,在这种情况下,您可能最好使用其中一个的现有实现。