2015-04-12 50 views
7

我期待循环通过蓝鸟一些任务,只是使用超时作为实验机制。 [不希望使用异步或任何其他库]循环通过任务瀑布 - 承诺蓝鸟

var Promise = require('bluebird'); 

var fileA = { 
    1: 'one', 
    2: 'two', 
    3: 'three', 
    4: 'four', 
    5: 'five' 
}; 


function calculate(key) { 
    return new Promise(function (resolve, reject) { 
     setTimeout(function() { 
      resolve(fileA[key]); 
     }, 500); 
    }); 
} 

Promise.map(Object.keys(fileA), function (key) { 
    calculate(key).then(function (res) { 
     console.log(res); 
    }); 
}).then(function() { 
    console.log('finish'); 
}); 

结果是

finish, 
one, 
two, 
three, 
four, 
five, 

我需要循环只迭代一次,每次为完成超时,那么火与完成最后thenable。

+0

如果Promise.map工作如何,我认为它在蓝鸟,一个简单的'return'应该修复它(之前'计算(键)。然后(函数(RES){') – orhanhenrik

回答

15
  1. 传递给Promise.map函数对象,你需要返回一个对象的承诺,让所有的承诺将得到解决,解决的值数组可以被传递到下一个then功能。在你的情况下,因为你没有明确地返回任何东西,undefined将被返回,默认情况下,不是承诺。因此,finish的可执行函数被执行,因为Promises.map的承诺已用undefined解决。您可以确认这样

    ... 
    }).then(function (result) { 
        console.log(result); 
        console.log('finish'); 
    }); 
    

    将打印

    [ undefined, undefined, undefined, undefined, undefined ] 
    finish 
    one 
    two 
    three 
    four 
    five 
    

    所以,你的代码应该有一个return声明这样

    Promise.map(Object.keys(fileA), function (key) { 
        return calculate(key).then(function (res) { 
         console.log(res); 
        }); 
    }).then(function() { 
        console.log('finish'); 
    }); 
    

    现在,你会看到代码打印的东西在订单中,当我们返回Promise对象并且finish的可选函数在所有Promise被解析后被调用。但他们都没有顺序解决。如果发生这种情况,每个数字都会在指定时间过后打印。这将我们带到了第二部分。

  2. Promise.map将执行作为参数传递的函数,只要解析数组中的Promises即可。引用文档时,

    尽快调用给定项目的映射函数,也就是说,当输入数组中的项目索引的承诺得到满足时。

    因此,数组中的所有值都转换为Promises,并用相应的值解析,并且函数将立即为每个值调用。所以,他们都在同一时间等待500毫秒并立即解决。这不会顺序发生。

    由于您希望它们按顺序执行,因此您需要使用Promise.each。引用文档

    连续发生迭代。 ....如果迭代器函数返回一个promise或一个thenable,那么在继续下一次迭代之前,等待promise的结果。

    由于承诺是连续创建的,并且在继续之前等待解析,所以结果的顺序是有保证的。所以,你的代码应该成为

    Promise.each(Object.keys(fileA), function (key) { 
        return calculate(key).then(function (res) { 
         console.log(res); 
        }); 
    }).then(function() { 
        console.log('finish'); 
    }); 
    

    附加说明:

    如果顺序并不重要,本杰明Gruenbaum的建议,你可以用Promise.map本身,与concurrency limit,这样

    Promise.map(Object.keys(fileA), function (key) { 
        return calculate(key).then(function (res) { 
         console.log(res); 
        }); 
    }, { concurrency: 1 }).then(function() { 
        console.log('finish'); 
    }); 
    

    concurrency选项基本上限制了在创建之前可以创建和解析的Promise的数量更多的承诺。因此,在这种情况下,由于限制为1,它将创建第一个承诺,并且在达到限制时,它将等待创建的Promise解决,然后继续执行下一个Promise。


如果使用calculate整点是引入延迟,然后我建议Promise.delay,其可以这样

Promise.each(Object.keys(fileA), function (key) { 
    return Promise.delay(500).then(function() { 
     console.log(fileA[key]); 
    }); 
}).then(function() { 
    console.log('finish'); 
}); 

delay可以透明链的解析值被用于承诺到下一个可靠的功能,所以代码可以缩短为

Promise.each(Object.keys(fileA), function (key) { 
    return Promise.resolve(fileA[key]).delay(500).then(console.log); 
}).then(function() { 
    console.log('finish'); 
}); 

由于Promise.delay接受一个动态值,你可以简单地写相同

Promise.each(Object.keys(fileA), function (key) { 
    return Promise.delay(fileA[key], 500).then(console.log); 
}).then(function() { 
    console.log('finish'); 
}); 

如果无极链到此为止本身,它能够更好地使用.done()方法来标记它,这样

... 
}).done(function() { 
    console.log('finish'); 
}); 

常规注意事项:如果您不打算在可执行的功能中执行任何处理,但您只是用它来跟踪进度或遵循该过程,则你可以更好地将它们改为Promise.tap。所以,你的代码将成为

Promise.each(Object.keys(fileA), function (key) { 
    return Promise.delay(fileA[key], 500).tap(console.log); 
}).then(function() { 
    // Do processing 
    console.log('finish'); 
}); 
+1

'.tap()'会比'console.log'的'.then()'更好,因为它不会影响已解析的值(所以最后的'.then()'可以使用已解析的值)。 –