我正在使用包含四个异步功能的这段代码。
我需要他们严格执行。
我该如何执行它们将按照示例中给出的顺序执行?
我的用例在Lambda中,并且可以访问异步。
function scanProducts() {
dynamoClient.scan(productParams, function (err, data) {
});
}
function scanCoupons() {
dynamoClient.scan(couponsParams, function (err, data) {
});
}
function scanRetailers() {
dynamoClient.scan(retailerParams, function (err, data) {
});
}
function sendEmail(ses) {
var email = {
"Source": "test@gmail.com",
"Template": "test-template",
"Destination": {
"ToAddresses": ["test@gmail.com"]
},
"TemplateData": `{}`
}
ses.sendTemplatedEmail(email);
}
答案 0 :(得分:5)
我会将dynamoClient.scan
转换为基于Promise的函数,然后await
对其进行每次调用,例如:
const dynamoClientScanProm = (params) => new Promise((resolve, reject) => {
dynamoClient.scan(params, function (err, data) {
if (err) reject(err);
else resolve(data);
});
});
// ...
// in an async function:
try {
await dynamoClientScanProm(productParams);
await dynamoClientScanProm(couponsParams);
await dynamoClientScanProm(retailerParams);
// promisify/await this too, if it's asynchronous
ses.sendTemplatedEmail(email);
} catch(e) {
// handle errors
}
尚不清楚您是否需要使用调用结果,但是如果您确实需要结果并且不只需要等待Promise解析,则在await
处理时将其分配给一个变量,例如
const productResults = await dynamoClientScanProm(productParams);
也就是说,如果dynamoClientScanProm
的其他调用未使用结果,则并行运行所有调用(使用Promise.all
而不是串行运行更有意义),这样就可以更快地完成整个过程。
答案 1 :(得分:2)
您可以按照Symbol.iterator
使用for await
来执行诺言的异步执行。可以将其打包到一个构造函数中,在示例示例中,它被称为Serial
(因为我们要按顺序逐个处理promises)
function Serial(promises = []) {
return {
promises,
resolved: [],
addPromise: function(fn) {
promises.push(fn);
},
resolve: async function(cb = i => i, err = (e) => console.log("trace: Serial.resolve " + e)) {
try {
for await (let p of this[Symbol.iterator]()) {}
return this.resolved.map(cb);
} catch (e) {
err(e);
}
},
[Symbol.iterator]: async function*() {
this.resolved = [];
for (let promise of this.promises) {
let p = await promise().catch(e => console.log("trace: Serial[Symbol.iterator] ::" + e));
this.resolved.push(p);
yield p;
}
}
}
}
Serial
的构造函数。 Serial.promises
Serial.resolved
中-这将存储已解决的承诺请求。addPromise
:获取一个返回Promise的函数并将其添加到Serial.promises
resolve
:异步调用自定义Symbol.iterator
。该iterator
会经历每个单独的承诺,等待其完成,然后将其添加到Serial.resolved
中。完成此操作后,它将返回对填充的Serial.resolved
数组起作用的map函数。这使您可以简单地调用resolve
,然后在响应中提供有关成员处理的回调。如果将诺言返回给该函数,则可以传递一个then
函数以得到整个数组。一个例子:
promises.resolve((resolved_request) => {
//do something with each resolved request
return resolved_request;
}).then((all_resolved_requests) => {
// do something with all resolved requests
});
下面的示例显示了如何将其有效地使用,无论您希望在每个单独的分辨率上发生什么,还是等到一切完成。
请注意,它们将始终保持顺序。这可以通过以下事实证明:第一个计时器的计数最高ms
。第二个Promise 将不会开始,直到第一个Promise结束,第三个Promise不会在第二个Promise完成之前开始,等等。
这使我很重要。尽管按顺序序列化您的承诺是有效的,但重要的是要意识到,如果其中的任何一个花费了任何时间,这将延迟您对数据的响应。 Parallel的优点在于,如果一切顺利,所有请求将花费更短的时间来完成。如果应用程序具有多个必需的请求,那么诸如序列化之类的东西非常有用,如果其中一个不可用,或者一个项目依赖于另一个项目(相当常见),则整个过程将会失败。
//helpers
let log = console.log.bind(console),
promises = Serial(),
timer = (tag, ms) => () => new Promise(res => {
setTimeout(() => {
res("finished " + tag);
}, ms) });
function Serial(promises = []) {
return {
promises,
resolved: [],
addPromise: function(fn) {
promises.push(fn);
},
resolve: async function(cb = i => i, err = (e) => console.log("trace: Serial.resolve " + e)) {
try {
for await (let p of this[Symbol.iterator]()) {}
return this.resolved.map(cb);
} catch (e) {
err(e);
}
},
[Symbol.iterator]: async function*() {
this.resolved = [];
for (let promise of this.promises) {
let p = await promise().catch(e => console.log("trace: Serial[Symbol.iterator] ::" + e));
this.resolved.push(p);
yield p;
}
}
}
}
promises.addPromise(timer(1, 3000));
promises.addPromise(timer(2, 1000));
promises.addPromise(timer(3, 2000));
promises
.resolve(msg => ( log(msg), msg) )
.then((complete) => log("everything is complete: " + complete));
通过使用一个迭代器依次调用每个promise
,我们可以确定他们 被依次收到。
尽管许多人没有意识到,Symbol.iterator
比标准for
循环更强大。这有两个主要原因。
第一个原因(在这种情况下适用)是因为它允许异步调用,这会影响所应用对象的状态。
第二个原因是它可用于从同一对象提供两种不同类型的数据。 A.e.您可能有一个要读取以下内容的数组:
let arr = [1,2,3,4];
您可以使用for
循环或forEach
来获取数据:
arr.forEach(v => console.log(v));
// 1, 2, 3, 4
但是如果您调整迭代器:
arr[Symbol.iterator] = function* () {
yield* this.map(v => v+1);
};
您得到了:
arr.forEach(v => console.log(v));
// 1, 2, 3, 4
for(let v of arr) console.log(v);
// 2, 3, 4, 5
由于许多不同的原因,这很有用,包括为请求加盖时间戳/映射引用等。如果您想了解更多信息,请查看ECMAScript文档:For in and For Of Statements
可以通过调用返回Promises的函数数组的构造函数来使用它。您还可以使用
将功能承诺添加到对象new Serial([])
.addPromise(() => fetch(url))
在您使用.resolve
方法之前,它不会运行功能承诺。
这意味着您可以在对异步调用执行任何操作之前随意添加Promise。 A.e.这两个是相同的:
使用addPromise:
let promises = new Serial([() => fetch(url), () => fetch(url2), () => fetch(url3)]);
promises.addPromise(() => fetch(url4));
promises.resolve().then((responses) => responses)
没有addPromise:
let promises = new Serial([() => fetch(url), () => fetch(url2), () => fetch(url3), () => fetch(url4)])
.resolve().then((responses) => responses)
下面是调整代码以按顺序执行操作的示例。事实是,您实际上并未提供很多入门代码,因此我将您的scan
函数替换为先前示例中使用的timer
函数。
要使用您的代码来实现此功能,您要做的就是从scan
函数中返回一个Promise,它将很好地工作:)
function Serial(promises = []) {
return {
promises,
resolved: [],
addPromise: function(fn) {
promises.push(fn);
},
resolve: async function(cb = i => i, err = (e) => console.log("trace: Serial.resolve " + e)) {
try {
for await (let p of this[Symbol.iterator]()) {}
return this.resolved.map(cb);
} catch (e) {
err(e);
}
},
[Symbol.iterator]: async function*() {
this.resolved = [];
for (let promise of this.promises) {
let p = await promise().catch(e => console.log("trace: Serial[Symbol.iterator] ::" + e));
this.resolved.push(p);
yield p;
}
}
}
}
const timer = (tag, ms) => new Promise(res => {
setTimeout(() => {
res("finished " + tag);
}, ms)
});
function scanProducts() {
return timer("products", 3000);
}
function scanCoupons() {
return timer("coupons", 1000);
}
async function scanRetailers() {
return timer("retailers", 2500);
}
function sendEmail(ses) {
var email = {
"Source": "test@gmail.com",
"Template": "test-template",
"Destination": {
"ToAddresses": ["test@gmail.com"]
},
"TemplateData": `{}`
}
ses.sendTemplatedEmail(email);
}
let promises = Serial([scanProducts, scanCoupons, scanRetailers]);
promises.resolve().then(resolutions => console.log(resolutions));
答案 2 :(得分:0)
使用异步系列。它尽可能简单地依次执行一系列回调。
<match **>
@type elasticsearch
index_name whatever.${key1}
<buffer tag, key1>
logstash_prefix ${key1}
</buffer>
</match>