2016-12-13 3 views
0

Итак, я пытаюсь моделировать некоторые длинные вычисления. для этого я вычисляю число фибоначчи. В случае, когда вычисление занимает много времени, мне нужно отклонить его.обещание Bluebird: почему это не тайм-аут?

Вопрос: Почему TimeoutErrror обработчик не работает? Как исправить код?

const expect = require('chai').expect 
const Promise = require('bluebird') 

function profib(n, prev = '0', cur = '1') { 
    return new Promise.resolve(n < 2) 
     .then(function(isTerm) { 
     if(isTerm) { 
      return cur 
     } else { 
      n = n - 2 
      return profib(n, cur, strAdd(cur, prev)); 
     } 
     }) 
    } 

const TIMEOUT = 10000 
const N = 20000 

describe('recursion', function() { 
    it.only('cancelation', function() { 
    this.timeout(2 * TIMEOUT) 
    let prom = profib(N).timeout(1000) 
     .catch(Promise.TimeoutError, function(e) { 
     console.log('timeout', e) 
     return '-1' 
     }) 

    return prom.then((num) => { 
     expect(num).equal('-1') 
    }) 
    }) 
}) 

const strAdd = function(lnum, rnum) { 
    lnum = lnum.split('').reverse(); 
    rnum = rnum.split('').reverse(); 
    var len = Math.max(lnum.length, rnum.length), 
     acc = 0; 
     res = []; 
    for(var i = 0; i < len; i++) { 
    var subres = Number(lnum[i] || 0) + Number(rnum[i] || 0) + acc; 
    acc = ~~(subres/10); // integer division 
    res.push(subres % 10); 
    } 
    if (acc !== 0) { 
    res.push(acc); 
    } 
    return res.reverse().join(''); 
}; 

Некоторая информация об окружающей среде:

➜ node -v 
v6.3.1 
➜ npm list --depth=0 
├── [email protected] 
├── [email protected] 
└── [email protected] 
+0

Используете ли вы Mocha ?, какой тестовый бегун вы используете? – Hosar

+0

добавлена ​​информация об окружающей среде – kharandziuk

+0

Единственный способ справиться с подобными вещами с помощью JavaScript - это сократить вашу обработку на небольшие шаги и позволить другому коду, чем ваш цикл, запускаться между этими шагами. Эффективный способ сделать это потребует рекурсивно-обещанных обещаний. Предпочтительно в WebWorker, чтобы вы не замедляли процесс пользовательского интерфейса. – Touffy

ответ

1

Если я читаю ваш код правильно profib не не выйти, пока она будет закончена.

Время ожидания не прерывается. Это просто события, добавленные в список событий для запуска браузера/узла. Браузер/узел запускает следующее событие, когда заканчивается код для текущего события.

Пример:

setTimeout(function() { 
 
    console.log("timeout"); 
 
}, 1); 
 

 
for(var i = 0; i < 100000; ++i) { 
 
    console.log(i); 
 
}

Даже если таймаут устанавливается на 1 миллисекунду она не появляется, пока после завершения цикла (который занимает около 5 секунд на моей машине)

Вы можете видеть эту проблему в простой петле навсегда

const TIMEOUT = 10000 

describe('forever', function() { 
    it.only('cancelation', function() { 
    this.timeout(2 * TIMEOUT) 

    while(true) { } // loop forever 
    }) 
}) 

Заберись со своей средой, и ты увидишь, что она никогда не уходит. JavaScript не поддерживает прерывания, он поддерживает события только.

Что касается фиксации кода, вам необходимо вставить вызов setTimeout. Например, давайте изменим навсегда петлю, так что выходит (и, следовательно, позволяет другие события)

const TIMEOUT = 100 

function alongtime(n) { 
    return new Promise(function(resolve, reject) { 
    function loopTillDone() { 
     if (n) { 
     --n; 
     setTimeout(loopTillDone); 
     } else { 
     resolve(); 
     } 
    } 
    loopTillDone(); 
    }); 
} 


describe('forever', function() { 
    it.only('cancelation', function(done) { 
    this.timeout(2 * TIMEOUT) 

    alongtime(100000000).then(done); 
    }) 
}) 

К сожалению, с помощью SetTimeout действительно медленная операция и, возможно, не следует использовать в функции как profib. Я не знаю, что предложить.

+0

Вы пытались запустить код? :) – kharandziuk

+0

Да, я это сделал. Я получаю ту же проблему, что и цикл forever, потому что ваш код в основном будет работать до завершения. Если ваша функция 'profib' не выйдет во время ее вычисления, не будет возможности запуска таймаута. Таким образом, вы получите ответ, код будет считать, что не было таймаута (тайм-ауты запускаются только тогда, когда событие может быть обработано). Следовательно, вы не получите ошибку таймаута. – gman

+0

таймауты, вообще или для обещаний, действительно работают только на асинхронные вещи. Вы делаете сетевой запрос, ваш код выходит, другие события - это процессы, если вы не получаете ответное событие до события таймаута, тогда вы получаете свой тайм-аут. Но если вы вращаетесь внутри функции и никогда не выходите, нет никакого способа для тайм-аута. – gman

0

Проблема возникает из-за того, что обещания работают «жадным» способом (это мое собственное объяснение). По этой причине функция profib не освобождает цикл событий. Чтобы исправить эту проблему, мне нужно освободить цикл событий. Самый простой способ сделать это с Promise.delay():

function profib(n, prev = '0', cur = '1') { 
    return new Promise.resolve(n < 2) 
     .then(function(isTerm) { 
     if(isTerm) { 
      return cur 
     } else { 
      n = n - 2 
      return Promise.delay(0).then(() => profib(n, cur, strAdd(cur, prev)); 
     } 
     }) 
} 
0

GMan уже объяснил, почему ваша идея не работает. Простое и эффективное решение было бы добавить условие в цикле, который проверяет время и перерывы, как таким образом:

var deadline = Date.now() + TIMEOUT 

function profib(n, prev = '0', cur = '1') { 
    if (Date.now() >= deadline) throw new Error("timed out") 
    // your regular fib recursion here 
} 

Вызов profib либо в конце концов возвращает результат, или выдаст ошибку. Тем не менее, он будет блокировать запуск любого другого JavaScript во время вычисления. Асинхронное выполнение не является решением здесь. Или, по крайней мере, не все. Что нужно для таких задач с интенсивным процессором, так это WebWorker для запуска его в другом контексте JavaScript. Затем вы можете обернуть свой канал связи WebWorker в Promise, чтобы получить API, который вы предполагали изначально.

+0

. Бросание ошибки не является хорошей практикой в ​​этом случае. Я хочу отменить расчет и выполнить тайм-аут на сайте вызывающего абонента. И я могу это сделать: проверить мой ответ – kharandziuk

 Смежные вопросы

  • Нет связанных вопросов^_^