Xåp*_* - 238 javascript promise sequential q serial-processing
请考虑以下以串行/顺序方式读取文件数组的代码.readFiles返回一个promise,只有在按顺序读取所有文件后才会解析.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) =>
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start!
});
};
Run Code Online (Sandbox Code Playgroud)
上面的代码可以工作,但我不喜欢按顺序进行递归递归.是否有一种更简单的方法可以重写此代码,以便我不必使用我的奇怪readSequential功能?
最初我试图使用Promise.all,但这导致所有readFile调用同时发生,这不是我想要的:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
Run Code Online (Sandbox Code Playgroud)
Ben*_*aum 287
2017年更新:如果环境支持,我会使用异步功能:
async function readFiles(files) {
for(const file of files) {
await readFile(file);
}
};
Run Code Online (Sandbox Code Playgroud)
如果您愿意,可以推迟阅读文件,直到您需要使用异步生成器(如果您的环境支持它):
async function* readFiles(files) {
for(const file of files) {
yield await readFile(file);
}
};
Run Code Online (Sandbox Code Playgroud)
更新:在第二个想法 - 我可能会使用for循环:
var readFiles = function(files) {
var p = Promise.resolve(); // Q() in q
files.forEach(file =>
p = p.then(() => readFile(file));
);
return p;
};
Run Code Online (Sandbox Code Playgroud)
或者更紧凑,减少:
var readFiles = function(files) {
return files.reduce((p, file) => {
return p.then(() => readFile(file));
}, Promise.resolve()); // initial
};
Run Code Online (Sandbox Code Playgroud)
在其他的promise库中(比如when和Bluebird)你有实用的方法.
例如,Bluebird将是:
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 });
// if the order matters, you can use Promise.each instead and omit concurrency param
readAll.then(function(allFileContents){
// do stuff to read files.
});
Run Code Online (Sandbox Code Playgroud)
虽然今天没有理由不使用异步等待.
And*_*erg 64
以下是我更喜欢按顺序运行任务的方法.
function runSerial() {
var that = this;
// task1 is a function that returns a promise (and immediately starts executing)
// task2 is a function that returns a promise (and immediately starts executing)
return Promise.resolve()
.then(function() {
return that.task1();
})
.then(function() {
return that.task2();
})
.then(function() {
console.log(" ---- done ----");
});
}
Run Code Online (Sandbox Code Playgroud)
那些有更多任务的案件呢?喜欢,10?
function runSerial(tasks) {
var result = Promise.resolve();
tasks.forEach(task => {
result = result.then(() => task());
});
return result;
}
Run Code Online (Sandbox Code Playgroud)
joe*_*net 57
这个问题很老,但我们生活在ES6和功能JavaScript的世界中,所以让我们看看我们如何改进.
因为promises立即执行,我们不能只创建一个promises数组,它们都会并行启动.
相反,我们需要创建一个返回promise的函数数组.然后每个函数将按顺序执行,然后启动内部的promise.
我们可以通过几种方式解决这个问题,但我最喜欢的方法是使用reduce.
reduce与promises结合使用会变得有点棘手,所以我将下面的一个衬里分解成一些较小的可消化的咬合物.
此函数的本质是使用reduce以初始值start开头Promise.resolve([]),或者使用包含空数组的promise.
然后将此承诺传递给reduce方法as promise.这是按顺序将每个承诺链接在一起的关键.下一个执行的承诺是func,当then触发时,结果被连接,然后返回该承诺,执行reduce具有下一个promise函数的循环.
一旦所有承诺都已执行,返回的承诺将包含每个承诺的所有结果的数组.
ES6示例(一个班轮)
/*
* serial executes Promises sequentially.
* @param {funcs} An array of funcs that return promises.
* @example
* const urls = ['/url1', '/url2', '/url3']
* serial(urls.map(url => () => $.ajax(url)))
* .then(console.log.bind(console))
*/
const serial = funcs =>
funcs.reduce((promise, func) =>
promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))
Run Code Online (Sandbox Code Playgroud)
ES6示例(细分)
// broken down to for easier understanding
const concat = list => Array.prototype.concat.bind(list)
const promiseConcat = f => x => f().then(concat(x))
const promiseReduce = (acc, x) => acc.then(promiseConcat(x))
/*
* serial executes Promises sequentially.
* @param {funcs} An array of funcs that return promises.
* @example
* const urls = ['/url1', '/url2', '/url3']
* serial(urls.map(url => () => $.ajax(url)))
* .then(console.log.bind(console))
*/
const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))
Run Code Online (Sandbox Code Playgroud)
用法:
// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']
// next convert each item to a function that returns a promise
const funcs = urls.map(url => () => $.ajax(url))
// execute them serially
serial(funcs)
.then(console.log.bind(console))
Run Code Online (Sandbox Code Playgroud)
Shr*_*pta 35
要在ES6中完成此操作:
function(files) {
// Create a new empty promise (don't do that with real people ;)
var sequence = Promise.resolve();
// Loop over each file, and add on a promise to the
// end of the 'sequence' promise.
files.forEach(file => {
// Chain one computation onto the sequence
sequence =
sequence
.then(() => performComputation(file))
.then(result => doSomething(result));
// Resolves for each file, one at a time.
})
// This will resolve after the entire chain is resolved
return sequence;
}
Run Code Online (Sandbox Code Playgroud)
Poo*_*oya 24
标准Node.js的简单实用程序承诺:
function sequence(tasks, fn) {
return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve());
}
Run Code Online (Sandbox Code Playgroud)
UPDATE
items-promise是一个随时可用的NPM包.
Gil*_*ain 12
function downloadFile(fileUrl) { ... } // This function return a Promise
async function main()
{
var filesList = [...];
for (const file of filesList) {
await downloadFile(file);
}
}
Run Code Online (Sandbox Code Playgroud)
(必须使用for循环,而不是forEach因为 async/await 在 forEach 循环中运行时出现问题)
function downloadFile(fileUrl) { ... } // This function return a Promise
function downloadRecursion(filesList, index)
{
index = index || 0;
if (index < filesList.length)
{
downloadFile(filesList[index]).then(function()
{
index++;
downloadRecursion(filesList, index); // self invocation - recursion!
});
}
else
{
return Promise.resolve();
}
}
function main()
{
var filesList = [...];
downloadRecursion(filesList);
}
Run Code Online (Sandbox Code Playgroud)
Sal*_*ter 11
我不得不运行很多顺序任务,并使用这些答案来伪造一个能够处理任何顺序任务的函数......
function one_by_one(objects_array, iterator, callback) {
var start_promise = objects_array.reduce(function (prom, object) {
return prom.then(function () {
return iterator(object);
});
}, Promise.resolve()); // initial
if(callback){
start_promise.then(callback);
}else{
return start_promise;
}
}
Run Code Online (Sandbox Code Playgroud)
该函数需要2个参数+ 1个可选项.第一个参数是我们将要工作的数组.第二个参数是任务本身,一个返回一个promise的函数,下一个任务只有在这个promise解析时才会启动.第三个参数是在完成所有任务后运行的回调.如果没有传递回调,则该函数返回它创建的promise,以便我们可以处理结束.
这是一个用法示例:
var filenames = ['1.jpg','2.jpg','3.jpg'];
var resize_task = function(filename){
//return promise of async resizing with filename
};
one_by_one(filenames,resize_task );
Run Code Online (Sandbox Code Playgroud)
希望能节省一些时间......
首先,您需要了解 Promise 在创建时执行。
例如,如果您有代码:
["a","b","c"].map(x => returnsPromise(x))
Run Code Online (Sandbox Code Playgroud)
您需要将其更改为:
["a","b","c"].map(x => () => returnsPromise(x))
Run Code Online (Sandbox Code Playgroud)
然后我们需要按顺序链接 Promise:
["a", "b", "c"].map(x => () => returnsPromise(x))
.reduce(
(before, after) => before.then(_ => after()),
Promise.resolve()
)
Run Code Online (Sandbox Code Playgroud)
执行after(),将确保仅在时机成熟时才创建(并执行)promise。
我的首选解决方案:
function processArray(arr, fn) {
return arr.reduce(
(p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
Promise.resolve([])
);
}
Run Code Online (Sandbox Code Playgroud)
它与此处发布的其他内容没有根本区别,但是:
用法示例:
const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));
// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);
Run Code Online (Sandbox Code Playgroud)
在合理的当前 Chrome (v59) 和 NodeJS (v8.1.2) 上测试。
使用 ES2016 的 async/await(也许还有 ES2018 的某些功能),可以将其简化为以下形式:
function readFile(file) {
... // Returns a promise.
}
async function readFiles(files) {
for (file in files) {
await readFile(file)
}
}
Run Code Online (Sandbox Code Playgroud)
我还没有看到另一个答案表达了这种简单性。OP 表示不需要并行执行 readFile。然而,对于这样的 IO,在保持循环执行同步的同时,不阻塞单个文件的读取确实很有意义(在读取所有文件之前,您不想执行下一步)。由于我刚刚了解到这一点并且对此感到有点兴奋,因此我将分享 readFile 的并行异步执行与 readFiles 的整体同步执行的方法。
async function readFiles(files) {
await Promise.all(files.map(readFile))
}
Run Code Online (Sandbox Code Playgroud)
这难道不是一件美事吗?
这是上面另一个答案的轻微变化。使用原生 Promise:
function inSequence(tasks) {
return tasks.reduce((p, task) => p.then(task), Promise.resolve())
}
Run Code Online (Sandbox Code Playgroud)
解释
如果你有这些任务[t1, t2, t3],那么上面就相当于Promise.resolve().then(t1).then(t2).then(t3)。这是reduce的行为。
如何使用
首先你需要构建一个任务列表!任务是不接受任何参数的函数。如果您需要将参数传递给您的函数,则使用bind或 其他方法来创建任务。例如:
var tasks = files.map(file => processFile.bind(null, file))
inSequence(tasks).then(...)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
159653 次 |
| 最近记录: |