2017-02-09 8 views
0

Имеет ли следующий код javascript неопределенное поведение?

var resolve; 
 

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

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

Я написал код выше, чтобы достичь чего-то похожее на связанный список, в то время как данные в списке загружаются асинхронно.

Руководитель этого «связанного списка» - head. Каждый узел в списке имеет два поля: .data и .next, как обычный связанный список. .next - это обещание, которое будет разрешено для следующего узла в списке.

Каждый раз, когда вызывается addData(...), поле текущего последнего узла в списке разрешает новый узел и, таким образом, становится новым последним узлом.

Я проверил функциональность вышеуказанного кода в 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})

где решительность называется и назначается одновременно. Что должно произойти сперва, назначение или разрешение имени функции? Является ли это неопределенным поведением?

Спасибо!

+1

Независимо от того, работает оно или нет, он уверен, что он тупой (трудно читать и понимать точку кода). – jfriend00

+0

@ jfriend00 Он сохранил всю цепочку, если бы сохранил ссылку на начальную голову, которую он не делает. Конечно, легко ошибиться ... – Bergi

+0

@Bergi - Я уже удалил этот комментарий, потому что я пришел к выводу, что это слишком сложно понять, что происходит (по моему мнению, это плохой код). Но OP действительно сказал «как связанный список», но я думаю, что это вообще не связанный список. Я предполагаю, что это только более высокий охват кэша предыдущего обещания. Разве нет намного лучшего способа сделать это, что не так тупо и не использует переменную 'resolve' с более высоким охватом? – jfriend00

ответ

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, которую вы можете управлять с дополнительной переменной).

+0

Спасибо за ответ! Если мне нужно реализовать эту структуру «связанных списков», есть ли лучший/элегантный способ сделать это? –

0

Как отмечает Берги, это не неопределенное поведение, но есть способы реализовать это, что не совсем сбивает с толку.

Например, вы могли бы написать функцию addData так, чтобы он не назначает функцию resolve и вызвать его в том же заявлении:

function addData(d) { 
    let newResolve; 

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

    resolve = newResolve; 
} 

Похоже, что вы могли бы использовать очень окольным подход к создавая поток или наблюдаемую коллекцию, и в этом случае вам может быть просто лучше использовать существующую реализацию одного из них.

+0

Как хранить отложенное лучше, чем хранить функцию 'resolve'? – Bergi

+0

@Bergi Мне кажется немного понятнее, но справедливая точка. Я изменил свой ответ, чтобы избежать запутанной строки, которая вызывает и присваивает 'resolve' в том же самом выражении. – JLRishe

+0

Спасибо за ответ и предложения! Приложение очень сложно объяснить в нескольких словах. Я попытаюсь найти существующую реализацию, соответствующую требованиям .... –