猿问

如何按顺序执行四个异步函数?

我正在使用包含四个异步函数的这段代码。我需要它们以严格的顺序执行。

我该如何按照示例中给出的顺序执行该操作?

我的用例在 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);

    }


翻翻过去那场雪
浏览 164回答 3
3回答

catspeake

我将转换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 解析,则在awaiting时赋值给一个变量,例如const productResults = await dynamoClientScanProm(productParams);也就是说,如果 的其他调用没有使用结果dynamoClientScanProm,那么并行(使用Promise.all)而不是串行运行所有调用会更有意义,这样可以更快地完成整个过程。

qq_笑_17

回答:您可以使用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.它将返回 Promise 的函数数组作为参数。函数存储在 Serial.promises它存储了一个空数组Serial.resolved- 这将存储已解决的承诺请求。它有两种方法:addPromise: 接受一个返回 Promise 的函数并将其添加到 Serial.promisesresolve:异步调用自定义Symbol.iterator. 这iterator会遍历每一个 promise,等待它完成,然后将它添加到Serial.resolved. 完成后,它返回一个映射函数,该函数作用于填充的Serial.resolved数组。这使您可以简单地调用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 是有效的,但重要的是要意识到,如果它们占用任何一个需要任何时间,这将延迟您对数据的响应。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循环。这有两个重要原因。第一个原因,也是适用于这种情况的原因,是因为它允许异步调用会影响应用对象的状态。第二个原因是它可以用于提供来自同一对象的两种不同类型的数据。Ae 您可能有一个要读取其内容的数组: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, 4for(let v of arr) console.log(v);// 2, 3, 4, 5这对于许多不同的原因都很有用,包括时间戳请求/映射引用等。如果您想了解更多信息,请查看 ECMAScript 文档:For in 和 For Of 语句用:它可以通过使用返回 Promise 的函数数组调用构造函数来使用。您还可以通过使用向对象添加函数承诺new Serial([]).addPromise(() => fetch(url))在您使用该.resolve方法之前,它不会运行函数承诺。这意味着您可以在对异步调用执行任何操作之前,根据需要添加临时承诺。Ae 这两个是一样的:使用 addPromise: let promises = new Serial([() => fetch(url), () => fetch(url2), () => fetch(url3)]);  promises.addPromise(() => fetch(url4)); promises.resolve().then((responses) => responses)没有添加承诺: 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));

哆啦的时光机

使用异步系列。它按顺序运行一系列回调,尽可能简单。series([  function(done) {  console.log('first thing')  done()  },  function(done) {  console.log('second thing')  done(new Error('another thing'))  },  function(done) {  // never happens, because "second thing"  // passed an error to the done() callback  }  ], function(err) {  console.log(err.message) // "another thing"  })
随时随地看视频慕课网APP

相关分类

JavaScript
我要回答