为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

翻翻过去那场雪 2021-08-26 15:33:48
我正在使用包含四个异步函数的这段代码。我需要它们以严格的顺序执行。我该如何按照示例中给出的顺序执行该操作?我的用例在 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);    }
查看完整描述

3 回答

?
catspeake

TA贡献1111条经验 获得超0个赞

我将转换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)而不是串行运行所有调用会更有意义,这样可以更快地完成整个过程。


查看完整回答
反对 回复 2021-08-26
?
qq_笑_17

TA贡献1818条经验 获得超7个赞

回答:

您可以使用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.promises

    • resolve:异步调用自定义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, 4

for(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));


查看完整回答
反对 回复 2021-08-26
?
哆啦的时光机

TA贡献1779条经验 获得超6个赞

使用异步系列。它按顺序运行一系列回调,尽可能简单。


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"

  })


查看完整回答
反对 回复 2021-08-26
  • 3 回答
  • 0 关注
  • 163 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信