链接异步承诺1解决和1拒绝?

时间:2015-11-23 19:12:24

标签: javascript promise

我有一个功能,必须在几个步骤中执行异步操作。在每一步都可能失败。它可能在步骤1之前失败,因此您可能立即知道结果,或者在1.5秒之后。 失败时,必须运行回调。同意 成功时。 (我正在使用 时,因为它不仅仅是如果:时机很重要。)

我认为Promise是完美的,因为异步并且它们只解决了一次,但它仍然有问题:什么时候失败?我可以明确地看到它何时成功(在最后一步之后),但什么时候失败?在任何步骤内/之前。

这就是我现在所拥有的,但那太荒谬了:

function clickSpeed() {
    return new Promise(function(resolve, reject) {
        if ( test('one') ) {
            return setTimeout(function() {
                if ( test('two') ) {
                    return setTimeout(function() {
                        if ( test('three') ) {
                            return setTimeout(function() {
                                console.log('resolving...');
                                resolve();
                            }, 500);
                        }
                        console.log('rejecting...');
                        reject();
                    }, 500);
                }
                console.log('rejecting...');
                reject();
            }, 500);
        }
        console.log('rejecting...');
        reject();
    });
}

test()随机传递或失败一步。)

在这里小提琴:http://jsfiddle.net/rudiedirkx/zhdrjjx1/

我猜测解决方案是链接承诺,解决或拒绝每一步......?也许。那是一件事吗?我该如何实现呢?

这可以用于未知数量的步骤吗?

2 个答案:

答案 0 :(得分:1)

首先,让我们巧妙地重新安排开场陈述,以反映更为典型和更恰当的使用承诺。

而不是:

  

一个必须执行异步的功能,只需几步

让我们说:

  

一个必须在几个异步步骤

中执行某些操作的函数

因此,我们可能首先选择编写一个执行promise链并返回其结果的函数的函数:

function doAnAsyncSequence() {
    return Promise.resolve()
    .then(function() {
        doSomethingAsync('one');
    })
    .then(function() {
        doSomethingAsync('two');
    })
    .then(function() {
        doSomethingAsync('three');
    });
}

并且,为了演示,我们可以编写doSomethingAsync()以便它返回一个具有50:50被解决概率的承诺:被拒绝(这比延迟更有用):

function doSomethingAsync(x) {
    return new Promise(function(resolve, reject) {
        if(Math.random() > 0.5 ) {
            resolve(x);
        } else {
            reject(x); // importantly, this statement reports the input argument `x` as the reason for failure, which can be read and acted on where doSomethingAsync() is called.
        }
    });
}

然后,问题的核心部分:

  

什么时候失败?

可能会改写:

  

什么时候失败了?

这是一个更现实的问题,因为我们通常会调用我们几乎没有影响的异步进程(它们可能在世界其他地方的某个服务器上运行),我们希望它会成功但可能会随机失败。如果是这样,我们的代码(和/或我们的最终用户)想知道哪一个失败了,为什么。

doAnAsyncSequence()的情况下,我们可以这样做:

doAnAsyncSequence().then(function(result) {
    console.log(result); // if this line executes, it will always log "three", the result of the *last* step in the async sequence.
}, function(reason) {
    console.log('error: ' + reason);
});

虽然console.log()doAnAsyncSequence()中没有doSomethingAsync()声明:

  • 成功之后,我们可以观察到整体结果(在这个例子中总是"三")。
  • 出现错误时,我们确切知道哪个异步步骤导致进程失败("一个","两个"或"三个")。

Try it out

这就是理论。

回答具体问题(据我所知)......

代表doSomethingAsync(),写道:

function test_(value, delay) {
    return new Promise(function(resolve, reject) {
        //call your own test() function immediately
        var result = test(value);
        // resolve/reject the returned promise after a delay
        setTimeout(function() {
            result ? resolve() : reject(value);
        }, delay);
    });
}

代表doAnAsyncSequence(),写道:

function clickSpeed() {
    var delayAfterTest = 500;
    return Promise.resolve()
    .then(function() {
        test_('one', delayAfterTest);
    })
    .then(function() {
        test_('two', delayAfterTest);
    })
    .then(function() {
        test_('three', delayAfterTest);
    });
}

请致电如下:

clickSpeed().then(function() {
    console.log('all tests passed');
}, function(reason) {
    console.log('test sequence failed at: ' + reason);
});

答案 1 :(得分:1)

由于你的时间是单调的,而且你的工作时间很长。是预定义的,我重构代码以利用setInterval()和上下文来跟踪所需的工作"或者"步骤"。事实上,即使你仍然可以 使用来使用Promise,尽管你仍然可以,如果你想进一步链接处理程序,那么这是一个好主意:

function clickSpeed(resolve, reject) {
  var interval = setInterval(function(work) {
    try {
      var current = work.shift();
      if(!test(current)) { // Do current step's "work"
        clearInterval(interval); // reject on failure and clear interval
        console.log('rejecting...', current);
        reject();
      }
      else if(!work.length) { // If this was the last step
        clearInterval(interval); // resolve (success!) and clear interval
        console.log('resolving...');
        resolve();
      }
    }
    catch(ex) { // reject on exceptions as well
      reject(ex);
      clearInterval(interval);
    }
  }, 500, ['one', 'two', 'three']); // "work" array
}

该功能可以直接调用""使用resolve / reject处理程序,或用作Promise构造函数的参数。

请参见修改后的JSFiddle中的完整示例。

要解决Bergi的太多样板注释,可以更简洁地编写代码,而无需记录:

function clickSpeed(resolve, reject) {
    function done(success, val) {
        clearInterval(interval);
        success ? resolve(val) : reject(val);
    }

    var interval = setInterval(function(work) {
        try {
            if(test(work.shift()) || done(false)) {
                work.length || done(true);
            }
        }
        catch(ex) { // reject on exceptions as well
            done(false, ex);
        }
    }, 500, ['one', 'two', 'three']); // "work" array
}