ter = array_value($config, 'filter'); $arr = array_value($filter, $type); $enable = array_value($arr, 'enable'); $wordarr = array_value($arr, 'keyword'); if (0 == $enable || empty($wordarr)) return FALSE; foreach ($wordarr as $_keyword) { if (!$_keyword) continue; $r = strpos(strtolower($keyword), strtolower($_keyword)); if (FALSE !== $r) { $error = $_keyword; return TRUE; } } return FALSE; } // return http://domain.com OR https://domain.com function url_prefix() { $http = ((isset($_SERVER['HTTPS']) && 'on' == $_SERVER['HTTPS']) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://'; return $http . $_SERVER['HTTP_HOST']; } // 唯一身份ID function uniq_id() { return uniqid(substr(md5(microtime(true) . mt_rand(1000, 9999)), 8, 8)); } // 生成订单号 14位 function trade_no() { $trade_no = str_replace('.', '', microtime(1)); $strlen = mb_strlen($trade_no, 'UTF-8'); $strlen = 14 - $strlen; $str = ''; if ($strlen) { for ($i = 0; $i <= $strlen; $i++) { if ($i < $strlen) $str .= '0'; } } return $trade_no . $str; } // 生成订单号 16位 function trade_no_16() { $explode = explode(' ', microtime()); $trade_no = $explode[1] . mb_substr($explode[0], 2, 6, 'UTF-8'); return $trade_no; } // 当前年的天数 function date_year($time = NULL) { $time = intval($time) ? $time : time(); return date('L', $time) + 365; } // 当前年份中的第几天 function date_z($time = NULL) { $time = intval($time) ? $time : time(); return date('z', $time); } // 当前月份中的第几天,没有前导零 1 到 31 function date_j($time = NULL) { $time = intval($time) ? $time : time(); return date('j', $time); } // 当前月份中的第几天,有前导零的2位数字 01 到 31 function date_d($time = NULL) { $time = intval($time) ? $time : time(); return date('d', $time); } // 当前时间为星期中的第几天 数字表示 1表示星期一 到 7表示星期天 function date_w_n($time = NULL) { $time = intval($time) ? $time : time(); return date('N', $time); } // 当前日第几周 function date_d_w($time = NULL) { $time = intval($time) ? $time : time(); return date('W', $time); } // 当前几月 没有前导零1-12 function date_n($time = NULL) { $time = intval($time) ? $time : time(); return date('n', $time); } // 当前月的天数 function date_t($time = NULL) { $time = intval($time) ? $time : time(); return date('t', $time); } // 0 o'clock on the day function clock_zero() { return strtotime(date('Ymd')); } // 24 o'clock on the day function clock_twenty_four() { return strtotime(date('Ymd')) + 86400; } // 8点过期 / expired at 8 a.m. function eight_expired($time = NULL) { $time = intval($time) ? $time : time(); // 当前时间大于8点则改为第二天8点过期 $life = date('G') <= 8 ? (strtotime(date('Ymd')) + 28800 - $time) : clock_twenty_four() - $time + 28800; return $life; } // 24点过期 / expired at 24 a.m. function twenty_four_expired($time = NULL) { $time = intval($time) ? $time : time(); $twenty_four = clock_twenty_four(); $life = $twenty_four - $time; return $life; } /** * @param $url 提交地址 * @param string $post POST数组 / 空为GET获取数据 / $post='GET'获取连续跳转最终URL * @param string $cookie cookie * @param int $timeout 超时 * @param int $ms 设为1是毫秒 * @return mixed 返回数据 */ function https_request($url, $post = '', $cookie = '', $timeout = 30, $ms = 0) { if (empty($url)) return FALSE; if (version_compare(PHP_VERSION, '5.2.3', '<')) { $ms = 0; $timeout = 30; } is_array($post) and $post = http_build_query($post); // 没有安装curl 使用http的形式,支持post if (!extension_loaded('curl')) { //throw new Exception('server not install CURL'); if ($post) { return https_post($url, $post, $cookie, $timeout); } else { return http_get($url, $cookie, $timeout); } } is_array($cookie) and $cookie = http_build_query($cookie); $curl = curl_init(); // 返回执行结果,不输出 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); //php5.5跟php5.6中的CURLOPT_SAFE_UPLOAD的默认值不同 if (class_exists('\CURLFile')) { curl_setopt($curl, CURLOPT_SAFE_UPLOAD, true); } else { defined('CURLOPT_SAFE_UPLOAD') and curl_setopt($curl, CURLOPT_SAFE_UPLOAD, false); } // 设定请求的RUL curl_setopt($curl, CURLOPT_URL, $url); // 设定返回信息中包含响应信息头 if (ini_get('safe_mode') && ini_get('open_basedir')) { // $post参数必须为GET if ('GET' == $post) { // 安全模式时将头文件的信息作为数据流输出 curl_setopt($curl, CURLOPT_HEADER, true); // 安全模式采用连续抓取 curl_setopt($curl, CURLOPT_NOBODY, true); } } else { curl_setopt($curl, CURLOPT_HEADER, false); // 允许跳转10次 curl_setopt($curl, CURLOPT_MAXREDIRS, 10); // 使用自动跳转,返回最后的Location curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); } $ua1 = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'; $ua = empty($_SERVER["HTTP_USER_AGENT"]) ? $ua1 : $_SERVER["HTTP_USER_AGENT"]; curl_setopt($curl, CURLOPT_USERAGENT, $ua); // 兼容HTTPS if (FALSE !== stripos($url, 'https://')) { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); //ssl版本控制 //curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); curl_setopt($curl, CURLOPT_SSLVERSION, true); } $header = array('Content-type: application/x-www-form-urlencoded;charset=UTF-8', 'X-Requested-With: XMLHttpRequest'); $cookie and $header[] = "Cookie: $cookie"; curl_setopt($curl, CURLOPT_HTTPHEADER, $header); if ($post) { // POST curl_setopt($curl, CURLOPT_POST, true); // 自动设置Referer curl_setopt($curl, CURLOPT_AUTOREFERER, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $post); } if ($ms) { curl_setopt($curl, CURLOPT_NOSIGNAL, true); // 设置毫秒超时 curl_setopt($curl, CURLOPT_TIMEOUT_MS, intval($timeout)); // 超时毫秒 } else { curl_setopt($curl, CURLOPT_TIMEOUT, intval($timeout)); // 秒超时 } //优先解析 IPv6 超时后IPv4 //curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_setopt($curl, CURLOPT_ENCODING, 'gzip'); // 返回执行结果 $output = curl_exec($curl); // 有效URL,输出URL非URL页面内容 CURLOPT_RETURNTRANSFER 必须为false 'GET' == $post and $output = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); curl_close($curl); return $output; } function save_image($img) { $ch = curl_init(); // 设定请求的RUL curl_setopt($ch, CURLOPT_URL, $img); // 设定返回信息中包含响应信息头 启用时会将头文件的信息作为数据流输出 //curl_setopt($ch, CURLOPT_HEADER, false); //curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER["HTTP_USER_AGENT"]); // true表示$html,false表示echo $html curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); //curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0); curl_setopt($ch, CURLOPT_ENCODING, 'gzip'); $output = curl_exec($ch); curl_close($ch); return $output; } // 计算字串宽度:剧中对齐(字体大小/字串内容/字体链接/背景宽度/倍数) function calculate_str_width($size, $str, $font, $width, $multiple = 2) { $box = imagettfbbox($size, 0, $font, $str); return ($width - $box[4] - $box[6]) / $multiple; } // 搜索目录下的文件 比对文件后缀 function search_directory($path) { if (is_dir($path)) { $paths = scandir($path); foreach ($paths as $val) { $sub_path = $path . '/' . $val; if ('.' == $val || '..' == $val) { continue; } else if (is_dir($sub_path)) { //echo '目录名:' . $val . '
'; search_directory($sub_path); } else { //echo ' 最底层文件: ' . $path . '/' . $val . '
'; $ext = strtolower(file_ext($sub_path)); if (in_array($ext, array('php', 'asp', 'jsp', 'cgi', 'exe', 'dll'), TRUE)) { echo '异常文件:' . $sub_path . '
'; } } } } } // 一维数组转字符串 $sign待签名字符串 $url为urlencode转码GET参数字符串 function array_to_string($arr, &$sign = '', &$url = '') { if (count($arr) != count($arr, 1)) throw new Exception('Does not support multi-dimensional array to string'); // 注销签名 unset($arr['sign']); // 排序 ksort($arr); reset($arr); // 转字符串做签名 $url = ''; $sign = ''; foreach ($arr as $key => $val) { if (empty($val) || is_array($val)) continue; $url .= $key . '=' . urlencode($val) . '&'; $sign .= $key . '=' . $val . '&'; } $url = substr($url, 0, -1); $url = htmlspecialchars($url); $sign = substr($sign, 0, -1); } // 私钥生成签名 function rsa_create_sign($data, $key, $sign_type = 'RSA') { if (!function_exists('openssl_sign')) throw new Exception('OpenSSL extension is not enabled'); if (!defined('OPENSSL_ALGO_SHA256')) throw new Exception('Only versions above PHP 5.4.8 support SHA256'); $key = wordwrap($key, 64, "\n", true); if (FALSE === $key) throw new Exception('Private Key Error'); $key = "-----BEGIN RSA PRIVATE KEY-----\n$key\n-----END RSA PRIVATE KEY-----"; if ('RSA2' == $sign_type) { openssl_sign($data, $sign, $key, OPENSSL_ALGO_SHA256); } else { openssl_sign($data, $sign, $key, OPENSSL_ALGO_SHA1); } // 加密 return base64_encode($sign); } // 公钥验证签名 function rsa_verify_sign($data, $sign, $key, $sign_type = 'RSA') { $key = wordwrap($key, 64, "\n", true); if (FALSE === $key) throw new Exception('Public Key Error'); $key = "-----BEGIN PUBLIC KEY-----\n$key\n-----END PUBLIC KEY-----"; // 签名正确返回1 签名不正确返回0 错误-1 if ('RSA2' == $sign_type) { $result = openssl_verify($data, base64_decode($sign), $key, OPENSSL_ALGO_SHA256); } else { $result = openssl_verify($data, base64_decode($sign), $key, OPENSSL_ALGO_SHA1); } return $result === 1; } // Array to xml array('appid' => 'appid', 'code' => 'success') function array_to_xml($arr) { if (!is_array($arr) || empty($arr)) throw new Exception('Array Error'); $xml = ""; foreach ($arr as $key => $val) { if (is_numeric($val)) { $xml .= "<" . $key . ">" . $val . ""; } else { $xml .= "<" . $key . ">"; } } $xml .= ""; return $xml; } // Xml to array function xml_to_array($xml) { if (!$xml) throw new Exception('XML error'); $old = libxml_disable_entity_loader(true); // xml解析 $result = (array)simplexml_load_string($xml, null, LIBXML_NOCDATA | LIBXML_COMPACT); // 恢复旧值 if (FALSE === $old) libxml_disable_entity_loader(false); return $result; } // 逐行读取 function well_import($file) { if ($handle = fopen($file, 'r')) { while (!feof($handle)) { yield trim(fgets($handle)); } fclose($handle); } } // 计算总行数 function well_import_total($file, $key = 'well_import_total') { static $cache = array(); if (isset($cache[$key])) return $cache[$key]; $count = cache_get($key); if (NULL === $count) { $count = 0; $globs = well_import($file); while ($globs->valid()) { ++$count; $globs->next(); // 指向下一个 } $count and cache_set($key, $count, 300); } return $cache[$key] = $count; } $g_dir_file = FALSE; function well_search_dir($path) { global $g_dir_file; FALSE === $g_dir_file and $g_dir_file = array(); if (is_dir($path)) { $paths = scandir($path); foreach ($paths as $val) { $sub_path = $path . '/' . $val; if ('.' == $val || '..' == $val) { continue; } else if (is_dir($sub_path)) { well_search_dir($sub_path); } else { $g_dir_file[] = $sub_path; } } } return $g_dir_file; } ?>javascript - How do I execute four async functions in order? - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How do I execute four async functions in order? - Stack Overflow

programmeradmin0浏览0评论

I'm using this bit of code that contains four asynchronous functions. I need them to execute in a strict order.
How do I go about doing that where they will execute in the order given in the sample?
My use case is in a Lambda and I have access to async.

    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": "[email protected]",
        "Template": "test-template",
        "Destination": {
          "ToAddresses": ["[email protected]"]
        },
        "TemplateData": `{}`
      }

      ses.sendTemplatedEmail(email);
    }

I'm using this bit of code that contains four asynchronous functions. I need them to execute in a strict order.
How do I go about doing that where they will execute in the order given in the sample?
My use case is in a Lambda and I have access to async.

    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": "[email protected]",
        "Template": "test-template",
        "Destination": {
          "ToAddresses": ["[email protected]"]
        },
        "TemplateData": `{}`
      }

      ses.sendTemplatedEmail(email);
    }
Share Improve this question asked Jul 29, 2019 at 22:59 Marko NikolovMarko Nikolov 8352 gold badges11 silver badges17 bronze badges 2
  • use Continuation-Passing Style? – Robert Harvey Commented Jul 29, 2019 at 23:02
  • Step 1: Convert your asynchronous operations to return promises so you can use promises to manage the sequencing of your operations. Step 2: Learn how to sequence operations tracked with promises. A reference on the topic: How to synchronize a sequence of promises. – jfriend00 Commented Jul 30, 2019 at 0:01
Add a ment  | 

3 Answers 3

Reset to default 5

I'd convert the dynamoClient.scan to a Promise-based function, and then await each call of it, eg:

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
}

It's not clear if you need to use the result of the calls, but if you do need the result and don't just need to wait for the Promise to resolve, assign to a variable when awaiting, eg

const productResults = await dynamoClientScanProm(productParams);

That said, if the results aren't being used by the other calls of dynamoClientScanProm, it would make more sense to run all calls in parallel (using Promise.all), rather than in series, so that the whole process can be pleted sooner.

Answer:

You can use the Symbol.iterator in accordance with for await to perform asynchronous execution of your promises. This can be packaged up into a constructor, in the example case it's called Serial (because we're going through promises one by one, in order)

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;
            }
        }
    }
}

What is the above?

  • It's a constructor called Serial.
  • It takes as an argument an array of Functions that return Promises.
  • The functions are stored in Serial.promises
  • It has an empty array stored in Serial.resolved - this will store the resolved promise requests.
  • It has two methods:
    • addPromise: Takes a Function that returns a Promise and adds it to Serial.promises
    • resolve: Asynchronously calls a custom Symbol.iterator. This iterator goes through every single promise, waits for it to be pleted, and adds it to Serial.resolved. Once this is pleted, it returns a map function that acts on the populated Serial.resolved array. This allows you to simply call resolve and then provide a callback of what to do with the members in the response. If you return the promise to that function, you can pass a then function to be given the whole array.

An Example:

  promises.resolve((resolved_request) => { 
     //do something with each resolved request 

       return resolved_request;
  }).then((all_resolved_requests) => { 

     // do something with all resolved requests

  });

The below example shows how this can be used to great effect, whether you want something to happen on each individual resolution, or wait until everything is pleted.

Notice that they will always be in order. This is evident by the fact that the first timer is set with the highest ms count. The second Promise will not even begin until the first has pleted, the third won't begin before the second finishes, etc.

That brings me to an important point. Though having your Promises Serialized in order is effective, it's important to realize that this will delay your responses for your data if they take any one of them takes any amount of time. The beauty of Parallel is that, if all goes well, all requests take a shorter amount of time to plete. Something like Serialization is great for if an application has multiple required requests, and the whole thing will fail if one is not available, or if one item relies on another(pretty mon).

//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((plete) => log("everything is plete: " + plete));


How does it work?

By using an iterator that calls each promise one by one, we can be certain that they are received in order.

Although many people don't realize this Symbol.iterator is much more powerful than standard for loops. This is for two big reasons.

The first reason, and the one that is applicable in this situation, is because it allows for asynchronous calls that can affect the state of the applied object.

The second reason is that it can be used to provide two different types of data from the same object. A.e. You may have an array that you would like to read the contents of:

let arr = [1,2,3,4];

You can use a for loop or forEach to get the data:

arr.forEach(v => console.log(v)); 
// 1, 2, 3, 4

But if you adjust the iterator:

arr[Symbol.iterator] = function* () {
  yield* this.map(v => v+1);
};

You get this:

arr.forEach(v => console.log(v));
// 1, 2, 3, 4
for(let v of arr) console.log(v);
// 2, 3, 4, 5

This is useful for many different reasons, including timestamping requests/mapping references, etc. If you'd like to know more please take a look at the ECMAScript Documentation: For in and For Of Statements


Use:

It can be used by calling the constructor with an Array of functions that return Promises. You can also add Function Promises to the Object by using

new Serial([])
.addPromise(() => fetch(url))

It doesn't run the Function Promises until you use the .resolve method.

This means that you can add promises ad-hoc if you'd like before you do anything with the asynchronous calls. A.e. These two are the same:

With addPromise:

 let promises = new Serial([() => fetch(url), () => fetch(url2), () => fetch(url3)]); 
 promises.addPromise(() => fetch(url4));
 promises.resolve().then((responses) => responses)

Without addPromise:

 let promises = new Serial([() => fetch(url), () => fetch(url2), () => fetch(url3), () => fetch(url4)])
.resolve().then((responses) => responses)

Adjusting your Code:

Below is an Example of adjusting your code to do things in order. Thing is, you didn't really provide a whole lot of starter code so I substituted your scan function for the timer function I've used in previous examples.

To get this functioning using your code, all you would have to do is return a Promise from your scan function, and it will work perfectly :)

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": "[email protected]",
    "Template": "test-template",
    "Destination": {
      "ToAddresses": ["[email protected]"]
    },
    "TemplateData": `{}`
  }

  ses.sendTemplatedEmail(email);
}

let promises = Serial([scanProducts, scanCoupons, scanRetailers]);

promises.resolve().then(resolutions => console.log(resolutions));


Hope this helps! Happy Coding!

use async-series. It run a series of callbacks in sequence, as simply as possible.

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"
  })
发布评论

评论列表(0)

  1. 暂无评论