与发生器的异步循环

时间:2015-09-22 13:03:11

标签: javascript ecmascript-6

我必须按顺序运行一系列异步任务,每个任务之间有一段延迟,直到达到某个任务为止。我试着用es6发生器做这个。这是代码

function delay(callback, timeout) {
    return new Promise((resolve, reject) => setTimeout(() => {
        try {
            resolve(callback());
        } catch(e) {
            reject(e);
        }
    }, timeout));
}

function run(generatorFunc) {
    function continuer(verb, arg) {
        var result;
        try {
            result = generator[verb](arg);
        } catch (err) {
            return Promise.reject(err);
        }
        if (result.done) {
            return Promise.resolve(result.value);
        } else {
            return Promise.resolve(result.value).then(onFulfilled, onRejected);
        }
    }
    var generator = generatorFunc();
    var onFulfilled = continuer.bind(continuer, "next");
    var onRejected = continuer.bind(continuer, "throw");
    return onFulfilled();
}

run(function* () {
    var json;
    while (true) {
        json = yield delay(() => fetch('http://api.com/get.json').then(res => res.json()), 1000)
        if (json.isCompleted) {
            return json;
        }
    }
}).then(json => console.log(json));

现在,如果用户按下按钮,我想“破坏”该承诺链,如何在不引入全局变量的情况下做到这一点?感谢

1 个答案:

答案 0 :(得分:0)

{
  const run = task => {
    const taskState = task();
    let reject = null;
    const process = new Promise((rs, rj) => {
      (function next (dat) {
        const {value, done} = taskState.next(dat);
        (done) ? rs(value) : value.then(next).catch(reason => rj(reason));
      }());
      reject = rj;
    });
    //
    // Expose *hook* like what @Felix Kling suggested
    //  
    process.kill = sig => {
      taskState.return(); // close generator
      reject(sig); // reject promise
    };
    return process;
  }

  // To test interrupt, we create a task involving a async task which is 'idle' forever
  function* task () {
    // imagine big memory has been already allocated here
    // and one async task ... does not response ...
    yield new Promise(() => {}); /*pending forever*/
  }

  // setup process, we'll see 'KILL' or 'TIMEOUT' on console.error channel
  let process = run(task);
  process.catch(e => console.error('stderr', e)).then(/*never*/);

  // interrupt cases
  document.body.addEventListener('click', e => process.kill('KILL'));
  setTimeout(() => process.kill('TIMEOUT'), 10e3);
}