async 函数

含义

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

async 函数是什么?一句话,它就是 Generator 函数的语法糖。

前文有一个 Generator 函数,依次读取两个文件。

const fs = require('fs');

const readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};

const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

写成async函数,就是下面这样。

const asyncReadFile = async function () {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

async函数对 Generator 函数的改进,体现在以下四点。

(1)内置执行器。

Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。

asyncReadFile();

上面的代码调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。

(2)更好的语义。

asyncawait,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

(3)更广的适用性。

co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

(4)返回值是 Promise。

async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

基本用法

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

下面是一个例子。

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name);
  const stockPrice = await getStockPrice(symbol);
  return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
  console.log(result);
});

上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。

下面是另一个例子,指定多少毫秒后输出一个值。

function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);

上面代码指定 50 毫秒以后,输出hello world

由于async函数返回的是 Promise 对象,可以作为await命令的参数。所以,上面的例子也可以写成下面的形式。

async function timeout(ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);

async 函数有多种使用形式。

// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)

// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jake').then(…);

// 箭头函数
const foo = async () => {};

语法

async函数的语法规则总体上比较简单,难点是错误处理机制。

返回 Promise 对象

async函数返回一个 Promise 对象。

async函数内部return语句返回的值,会成为then方法回调函数的参数。

async function f() {
  return 'hello world';
}

f().then(v => console.log(v))
// "hello world"

上面代码中,函数f内部return命令返回的值,会被then方法回调函数接收到。

async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

async function f() {
  throw new Error('出错了');
}

f().then(
  v => console.log(v),
  e => console.log(e)
)
// Error: 出错了

Promise 对象的状态变化

async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

下面是一个例子。

async function getTitle(url) {
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/([\s\S]+)<\/title>/i</span>)[<span class="hljs-number" style="color:rgb(174,129,255);">1</span>];
}
getTitle(<span class="hljs-string" style="color:rgb(166,226,46);">'https://tc39.github.io/ecma262/'</span>).then(<span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log)
<span class="hljs-comment" style="color:rgb(117,113,94);">// "ECMAScript 2017 Language Specification"</span>
</code></pre> 
       <p>上面代码中,函数<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">getTitle</code>内部有三个操作:抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">then</code>方法里面的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">console.log</code>。</p> 
       <h3 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;">await 命令</h3> 
       <p>正常情况下,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令后面是一个 Promise 对象。如果不是,会被转成一个立即<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">resolve</code>的 Promise 对象。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-number" style="color:rgb(174,129,255);">123</span>;
}

f().then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">v</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(v))
<span class="hljs-comment" style="color:rgb(117,113,94);">// 123</span>
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令的参数是数值<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">123</code>,它被转成 Promise 对象,并立即<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">resolve</code>。</p> 
       <p><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令后面的 Promise 对象如果变为<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reject</code>状态,则<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reject</code>的参数会被<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">catch</code>方法的回调函数接收到。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.reject(<span class="hljs-string" style="color:rgb(166,226,46);">'出错了'</span>);
}

f()
.then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">v</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(v))
.catch(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">e</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(e))
<span class="hljs-comment" style="color:rgb(117,113,94);">// 出错了</span>
</code></pre> 
       <p>注意,上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>语句前面没有<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">return</code>,但是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reject</code>方法的参数依然传入了<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">catch</code>方法的回调函数。这里如果在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>前面加上<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">return</code>,效果是一样的。</p> 
       <p>只要一个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>语句后面的 Promise 变为<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reject</code>,那么整个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数都会中断执行。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.reject(<span class="hljs-string" style="color:rgb(166,226,46);">'出错了'</span>);
  <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.resolve(<span class="hljs-string" style="color:rgb(166,226,46);">'hello world'</span>); <span class="hljs-comment" style="color:rgb(117,113,94);">// 不会执行</span>
}
</code></pre> 
       <p>上面代码中,第二个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>语句是不会执行的,因为第一个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>语句状态变成了<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reject</code>。</p> 
       <p>有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>放在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">try...catch</code>结构里面,这样不管这个异步操作是否成功,第二个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>都会执行。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.reject(<span class="hljs-string" style="color:rgb(166,226,46);">'出错了'</span>);
  } <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span>(e) {
  }
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.resolve(<span class="hljs-string" style="color:rgb(166,226,46);">'hello world'</span>);
}

f()
.then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">v</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(v))
<span class="hljs-comment" style="color:rgb(117,113,94);">// hello world</span>
</code></pre> 
       <p>另一种方法是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>后面的 Promise 对象再跟一个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">catch</code>方法,处理前面可能出现的错误。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.reject(<span class="hljs-string" style="color:rgb(166,226,46);">'出错了'</span>)
    .catch(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">e</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(e));
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.resolve(<span class="hljs-string" style="color:rgb(166,226,46);">'hello world'</span>);
}

f()
.then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">v</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(v))
<span class="hljs-comment" style="color:rgb(117,113,94);">// 出错了</span>
<span class="hljs-comment" style="color:rgb(117,113,94);">// hello world</span>
</code></pre> 
       <h3 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;">错误处理</h3> 
       <p>如果<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>后面的异步操作出错,那么等同于<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数返回的 Promise 对象被<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reject</code>。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">new</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);">resolve, reject</span>) </span>{
    <span class="hljs-keyword" style="color:rgb(102,217,239);">throw</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">new</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Error</span>(<span class="hljs-string" style="color:rgb(166,226,46);">'出错了'</span>);
  });
}

f()
.then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">v</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(v))
.catch(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">e</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(e))
<span class="hljs-comment" style="color:rgb(117,113,94);">// Error:出错了</span>
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">f</code>执行后,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>后面的 Promise 对象会抛出一个错误对象,导致<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">catch</code>方法的回调函数被调用,它的参数就是抛出的错误对象。具体的执行机制,可以参考后文的“async 函数的实现原理”。</p> 
       <p>防止出错的方法,也是将其放在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">try...catch</code>代码块之中。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">new</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);">resolve, reject</span>) </span>{
      <span class="hljs-keyword" style="color:rgb(102,217,239);">throw</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">new</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Error</span>(<span class="hljs-string" style="color:rgb(166,226,46);">'出错了'</span>);
    });
  } <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span>(e) {
  }
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span>(<span class="hljs-string" style="color:rgb(166,226,46);">'hello world'</span>);
}
</code></pre> 
       <p>如果有多个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令,可以统一放在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">try...catch</code>结构中。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">main</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> val1 = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> firstStep();
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> val2 = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> secondStep(val1);
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> val3 = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> thirdStep(val1, val2);

    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-string" style="color:rgb(166,226,46);">'Final: '</span>, val3);
  }
  <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span> (err) {
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.error(err);
  }
}
</code></pre> 
       <p>下面的例子使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">try...catch</code>结构,实现多次重复尝试。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> superagent = <span class="hljs-built_in" style="color:rgb(249,38,114);">require</span>(<span class="hljs-string" style="color:rgb(166,226,46);">'superagent'</span>);
<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> NUM_RETRIES = <span class="hljs-number" style="color:rgb(174,129,255);">3</span>;

<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">test</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> i;
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> (i = <span class="hljs-number" style="color:rgb(174,129,255);">0</span>; i < NUM_RETRIES; ++i) {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
      <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> superagent.get(<span class="hljs-string" style="color:rgb(166,226,46);">'http://google.com/this-throws-an-error'</span>);
      <span class="hljs-keyword" style="color:rgb(102,217,239);">break</span>;
    } <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span>(err) {}
  }
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(i); <span class="hljs-comment" style="color:rgb(117,113,94);">// 3</span>
}

test();
</code></pre> 
       <p>上面代码中,如果<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>操作成功,就会使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">break</code>语句退出循环;如果失败,会被<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">catch</code>语句捕捉,然后进入下一轮循环。</p> 
       <h3 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;">使用注意点</h3> 
       <p>第一点,前面已经说过,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令后面的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">Promise</code>对象,运行结果可能是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">rejected</code>,所以最好把<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令放在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">try...catch</code>代码块中。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">myFunction</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> somethingThatReturnsAPromise();
  } <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span> (err) {
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(err);
  }
}

<span class="hljs-comment" style="color:rgb(117,113,94);">// 另一种写法</span>

<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">myFunction</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> somethingThatReturnsAPromise()
  .catch(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);">err</span>) </span>{
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(err);
  });
}
</code></pre> 
       <p>第二点,多个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> foo = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> getFoo();
<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> bar = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> getBar();
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">getFoo</code>和<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">getBar</code>是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">getFoo</code>完成以后,才会执行<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">getBar</code>,完全可以让它们同时触发。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-comment" style="color:rgb(117,113,94);">// 写法一</span>
<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> [foo, bar] = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.all([getFoo(), getBar()]);

<span class="hljs-comment" style="color:rgb(117,113,94);">// 写法二</span>
<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> fooPromise = getFoo();
<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> barPromise = getBar();
<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> foo = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> fooPromise;
<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> bar = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> barPromise;
</code></pre> 
       <p>上面两种写法,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">getFoo</code>和<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">getBar</code>都是同时触发,这样就会缩短程序的执行时间。</p> 
       <p>第三点,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令只能用在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数之中,如果用在普通函数,就会报错。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">dbFuc</span>(<span class="hljs-params" style="color:rgb(245,135,31);">db</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> docs = [{}, {}, {}];

  <span class="hljs-comment" style="color:rgb(117,113,94);">// 报错</span>
  docs.forEach(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);">doc</span>) </span>{
    <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> db.post(doc);
  });
}
</code></pre> 
       <p>上面代码会报错,因为<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>用在普通函数之中了。但是,如果将<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">forEach</code>方法的参数改成<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数,也有问题。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">dbFuc</span>(<span class="hljs-params" style="color:rgb(245,135,31);">db</span>) </span>{ <span class="hljs-comment" style="color:rgb(117,113,94);">//这里不需要 async</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> docs = [{}, {}, {}];

  <span class="hljs-comment" style="color:rgb(117,113,94);">// 可能得到错误结果</span>
  docs.forEach(<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);">doc</span>) </span>{
    <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> db.post(doc);
  });
}
</code></pre> 
       <p>上面代码可能不会正常工作,原因是这时三个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">db.post</code>操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for</code>循环。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">dbFuc</span>(<span class="hljs-params" style="color:rgb(245,135,31);">db</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> docs = [{}, {}, {}];

  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> doc <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> docs) {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> db.post(doc);
  }
}
</code></pre> 
       <p>如果确实希望多个请求并发执行,可以使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">Promise.all</code>方法。当三个请求都会<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">resolved</code>时,下面两种写法效果相同。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">dbFuc</span>(<span class="hljs-params" style="color:rgb(245,135,31);">db</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> docs = [{}, {}, {}];
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> promises = docs.map(<span class="hljs-function">(<span class="hljs-params" style="color:rgb(245,135,31);">doc</span>) =></span> db.post(doc));

  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> results = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.all(promises);
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(results);
}

<span class="hljs-comment" style="color:rgb(117,113,94);">// 或者使用下面的写法</span>

<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">dbFuc</span>(<span class="hljs-params" style="color:rgb(245,135,31);">db</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> docs = [{}, {}, {}];
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> promises = docs.map(<span class="hljs-function">(<span class="hljs-params" style="color:rgb(245,135,31);">doc</span>) =></span> db.post(doc));

  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> results = [];
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> promise <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> promises) {
    results.push(<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> promise);
  }
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(results);
}
</code></pre> 
       <p>目前,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">@std/esm</code>模块加载器支持顶层<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>,即<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令可以不放在 async 函数里面,直接使用。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-comment" style="color:rgb(117,113,94);">// async 函数的写法</span>
<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> start = <span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> () => {
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> res = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> fetch(<span class="hljs-string" style="color:rgb(166,226,46);">'google.com'</span>);
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> res.text();
};

start().then(<span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log);

<span class="hljs-comment" style="color:rgb(117,113,94);">// 顶层 await 的写法</span>
<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> res = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> fetch(<span class="hljs-string" style="color:rgb(166,226,46);">'google.com'</span>);
<span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> res.text());
</code></pre> 
       <p>上面代码中,第二种写法的脚本必须使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">@std/esm</code>加载器,才会生效。</p> 
       <h2 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;border-bottom:1px solid rgb(238,238,238);">async 函数的实现原理</h2> 
       <p>async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">fn</span>(<span class="hljs-params" style="color:rgb(245,135,31);">args</span>) </span>{
  <span class="hljs-comment" style="color:rgb(117,113,94);">// ...</span>
}

<span class="hljs-comment" style="color:rgb(117,113,94);">// 等同于</span>

<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">fn</span>(<span class="hljs-params" style="color:rgb(245,135,31);">args</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> spawn(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* (<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
    <span class="hljs-comment" style="color:rgb(117,113,94);">// ...</span>
  });
}
</code></pre> 
       <p>所有的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数都可以写成上面的第二种形式,其中的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">spawn</code>函数就是自动执行器。</p> 
       <p>下面给出<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">spawn</code>函数的实现,基本就是前文自动执行器的翻版。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">spawn</span>(<span class="hljs-params" style="color:rgb(245,135,31);">genF</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">new</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);">resolve, reject</span>) </span>{
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> gen = genF();
    <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">step</span>(<span class="hljs-params" style="color:rgb(245,135,31);">nextF</span>) </span>{
      <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> next;
      <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
        next = nextF();
      } <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span>(e) {
        <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> reject(e);
      }
      <span class="hljs-keyword" style="color:rgb(102,217,239);">if</span>(next.done) {
        <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> resolve(next.value);
      }
      <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.resolve(next.value).then(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);">v</span>) </span>{
        step(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{ <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> gen.next(v); });
      }, <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);">e</span>) </span>{
        step(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{ <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> gen.throw(e); });
      });
    }
    step(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{ <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> gen.next(<span class="hljs-literal" style="color:rgb(174,129,255);">undefined</span>); });
  });
}
</code></pre> 
       <h2 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;border-bottom:1px solid rgb(238,238,238);">与其他异步处理方法的比较</h2> 
       <p>我们通过一个例子,来看 async 函数与 Promise、Generator 函数的比较。</p> 
       <p>假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。</p> 
       <p>首先是 Promise 的写法。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">chainAnimationsPromise</span>(<span class="hljs-params" style="color:rgb(245,135,31);">elem, animations</span>) </span>{

  <span class="hljs-comment" style="color:rgb(117,113,94);">// 变量ret用来保存上一个动画的返回值</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> ret = <span class="hljs-literal" style="color:rgb(174,129,255);">null</span>;

  <span class="hljs-comment" style="color:rgb(117,113,94);">// 新建一个空的Promise</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> p = <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.resolve();

  <span class="hljs-comment" style="color:rgb(117,113,94);">// 使用then方法,添加所有动画</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span>(<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> anim <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> animations) {
    p = p.then(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);">val</span>) </span>{
      ret = val;
      <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> anim(elem);
    });
  }

  <span class="hljs-comment" style="color:rgb(117,113,94);">// 返回一个部署了错误捕捉机制的Promise</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> p.catch(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);">e</span>) </span>{
    <span class="hljs-comment" style="color:rgb(117,113,94);">/* 忽略错误,继续执行 */</span>
  }).then(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
    <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> ret;
  });

}
</code></pre> 
       <p>虽然 Promise 的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">then</code>、<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">catch</code>等等),操作本身的语义反而不容易看出来。</p> 
       <p>接着是 Generator 函数的写法。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">chainAnimationsGenerator</span>(<span class="hljs-params" style="color:rgb(245,135,31);">elem, animations</span>) </span>{

  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> spawn(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>*(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
    <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> ret = <span class="hljs-literal" style="color:rgb(174,129,255);">null</span>;
    <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
      <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span>(<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> anim <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> animations) {
        ret = <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> anim(elem);
      }
    } <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span>(e) {
      <span class="hljs-comment" style="color:rgb(117,113,94);">/* 忽略错误,继续执行 */</span>
    }
    <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> ret;
  });

}
</code></pre> 
       <p>上面代码使用 Generator 函数遍历了每个动画,语义比 Promise 写法更清晰,用户定义的操作全部都出现在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">spawn</code>函数的内部。这个写法的问题在于,必须有一个任务运行器,自动执行 Generator 函数,上面代码的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">spawn</code>函数就是自动执行器,它返回一个 Promise 对象,而且必须保证<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">yield</code>语句后面的表达式,必须返回一个 Promise。</p> 
       <p>最后是 async 函数的写法。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">chainAnimationsAsync</span>(<span class="hljs-params" style="color:rgb(245,135,31);">elem, animations</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> ret = <span class="hljs-literal" style="color:rgb(174,129,255);">null</span>;
  <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span>(<span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> anim <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> animations) {
      ret = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> anim(elem);
    }
  } <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span>(e) {
    <span class="hljs-comment" style="color:rgb(117,113,94);">/* 忽略错误,继续执行 */</span>
  }
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> ret;
}
</code></pre> 
       <p>可以看到 Async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。它将 Generator 写法中的自动执行器,改在语言层面提供,不暴露给用户,因此代码量最少。如果使用 Generator 写法,自动执行器需要用户自己提供。</p> 
       <h2 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;border-bottom:1px solid rgb(238,238,238);">实例:按顺序完成异步操作</h2> 
       <p>实际开发中,经常遇到一组异步操作,需要按照顺序完成。比如,依次远程读取一组 URL,然后按照读取的顺序输出结果。</p> 
       <p>Promise 的写法如下。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">logInOrder</span>(<span class="hljs-params" style="color:rgb(245,135,31);">urls</span>) </span>{
  <span class="hljs-comment" style="color:rgb(117,113,94);">// 远程读取所有URL</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> textPromises = urls.map(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">url</span> =></span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> fetch(url).then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">response</span> =></span> response.text());
  });

  <span class="hljs-comment" style="color:rgb(117,113,94);">// 按次序输出</span>
  textPromises.reduce(<span class="hljs-function">(<span class="hljs-params" style="color:rgb(245,135,31);">chain, textPromise</span>) =></span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> chain.then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">()</span> =></span> textPromise)
      .then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">text</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(text));
  }, <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.resolve());
}
</code></pre> 
       <p>上面代码使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">fetch</code>方法,同时远程读取一组 URL。每个<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">fetch</code>操作都返回一个 Promise 对象,放入<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">textPromises</code>数组。然后,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reduce</code>方法依次处理每个 Promise 对象,然后使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">then</code>,将所有 Promise 对象连起来,因此就可以依次输出结果。</p> 
       <p>这种写法不太直观,可读性比较差。下面是 async 函数实现。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">logInOrder</span>(<span class="hljs-params" style="color:rgb(245,135,31);">urls</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> url <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> urls) {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> response = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> fetch(url);
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> response.text());
  }
}
</code></pre> 
       <p>上面代码确实大大简化,问题是所有远程操作都是继发。只有前一个 URL 返回结果,才会去读取下一个 URL,这样做效率很差,非常浪费时间。我们需要的是并发发出远程请求。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">logInOrder</span>(<span class="hljs-params" style="color:rgb(245,135,31);">urls</span>) </span>{
  <span class="hljs-comment" style="color:rgb(117,113,94);">// 并发读取远程URL</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> textPromises = urls.map(<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> url => {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> response = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> fetch(url);
    <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> response.text();
  });

  <span class="hljs-comment" style="color:rgb(117,113,94);">// 按次序输出</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> textPromise <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> textPromises) {
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> textPromise);
  }
}
</code></pre> 
       <p>上面代码中,虽然<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">map</code>方法的参数是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数,但它是并发执行的,因为只有<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数内部是继发执行,外部不受影响。后面的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for..of</code>循环内部使用了<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>,因此实现了按顺序输出。</p> 
       <h2 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;border-bottom:1px solid rgb(238,238,238);">异步遍历器</h2> 
       <p>《遍历器》一章说过,Iterator 接口是一种数据遍历的协议,只要调用遍历器对象的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法,就会得到一个对象,表示当前遍历指针所在的那个位置的信息。<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法返回的对象的结构是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">{value, done}</code>,其中<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">value</code>表示当前的数据的值,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">done</code>是一个布尔值,表示遍历是否结束。</p> 
       <p>这里隐含着一个规定,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法必须是同步的,只要调用就必须立刻返回值。也就是说,一旦执行<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法,就必须同步地得到<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">value</code>和<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">done</code>这两个属性。如果遍历指针正好指向同步操作,当然没有问题,但对于异步操作,就不太合适了。目前的解决方法是,Generator 函数里面的异步操作,返回一个 Thunk 函数或者 Promise 对象,即<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">value</code>属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">done</code>属性则还是同步产生的。</p> 
       <p>目前,有一个提案,为异步操作提供原生的遍历器接口,即<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">value</code>和<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">done</code>这两个属性都是异步产生,这称为”异步遍历器“(Async Iterator)。</p> 
       <h3 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;">异步遍历的接口</h3> 
       <p>异步遍历器的最大的语法特点,就是调用遍历器的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法,返回的是一个 Promise 对象。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;">asyncIterator
  .next()
  .then(
    <span class="hljs-function">(<span class="hljs-params" style="color:rgb(245,135,31);">{ value, done }</span>) =></span> <span class="hljs-comment" style="color:rgb(117,113,94);">/* ... */</span>
  );
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">asyncIterator</code>是一个异步遍历器,调用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法以后,返回一个 Promise 对象。因此,可以使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">then</code>方法指定,这个 Promise 对象的状态变为<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">resolve</code>以后的回调函数。回调函数的参数,则是一个具有<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">value</code>和<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">done</code>两个属性的对象,这个跟同步遍历器是一样的。</p> 
       <p>我们知道,一个对象的同步遍历器的接口,部署在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">Symbol.iterator</code>属性上面。同样地,对象的异步遍历器接口,部署在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">Symbol.asyncIterator</code>属性上面。不管是什么样的对象,只要它的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">Symbol.asyncIterator</code>属性有值,就表示应该对它进行异步遍历。</p> 
       <p>下面是一个异步遍历器的例子。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> asyncIterable = createAsyncIterable([<span class="hljs-string" style="color:rgb(166,226,46);">'a'</span>, <span class="hljs-string" style="color:rgb(166,226,46);">'b'</span>]);
<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> asyncIterator = asyncIterable[<span class="hljs-built_in" style="color:rgb(249,38,114);">Symbol</span>.asyncIterator]();

asyncIterator
.next()
.then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">iterResult1</span> =></span> {
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(iterResult1); <span class="hljs-comment" style="color:rgb(117,113,94);">// { value: 'a', done: false }</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> asyncIterator.next();
})
.then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">iterResult2</span> =></span> {
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(iterResult2); <span class="hljs-comment" style="color:rgb(117,113,94);">// { value: 'b', done: false }</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> asyncIterator.next();
})
.then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">iterResult3</span> =></span> {
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(iterResult3); <span class="hljs-comment" style="color:rgb(117,113,94);">// { value: undefined, done: true }</span>
});
</code></pre> 
       <p>上面代码中,异步遍历器其实返回了两次值。第一次调用的时候,返回一个 Promise 对象;等到 Promise 对象<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">resolve</code>了,再返回一个表示当前数据成员信息的对象。这就是说,异步遍历器与同步遍历器最终行为是一致的,只是会先返回 Promise 对象,作为中介。</p> 
       <p>由于异步遍历器的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法,返回的是一个 Promise 对象。因此,可以把它放在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令后面。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> asyncIterable = createAsyncIterable([<span class="hljs-string" style="color:rgb(166,226,46);">'a'</span>, <span class="hljs-string" style="color:rgb(166,226,46);">'b'</span>]);
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> asyncIterator = asyncIterable[<span class="hljs-built_in" style="color:rgb(249,38,114);">Symbol</span>.asyncIterator]();
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> asyncIterator.next());
  <span class="hljs-comment" style="color:rgb(117,113,94);">// { value: 'a', done: false }</span>
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> asyncIterator.next());
  <span class="hljs-comment" style="color:rgb(117,113,94);">// { value: 'b', done: false }</span>
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> asyncIterator.next());
  <span class="hljs-comment" style="color:rgb(117,113,94);">// { value: undefined, done: true }</span>
}
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>处理以后,就不必使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">then</code>方法了。整个流程已经很接近同步处理了。</p> 
       <p>注意,异步遍历器的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法是可以连续调用的,不必等到上一步产生的 Promise 对象<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">resolve</code>以后再调用。这种情况下,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法会累积起来,自动按照每一步的顺序运行下去。下面是一个例子,把所有的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法放在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">Promise.all</code>方法里面。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> asyncGenObj = createAsyncIterable([<span class="hljs-string" style="color:rgb(166,226,46);">'a'</span>, <span class="hljs-string" style="color:rgb(166,226,46);">'b'</span>]);
<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> [{<span class="hljs-attr">value</span>: v1}, {<span class="hljs-attr">value</span>: v2}] = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>.all([
  asyncGenObj.next(), asyncGenObj.next()
]);

<span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(v1, v2); <span class="hljs-comment" style="color:rgb(117,113,94);">// a b</span>
</code></pre> 
       <p>另一种用法是一次性调用所有的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法,然后<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>最后一步操作。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> writer = openFile(<span class="hljs-string" style="color:rgb(166,226,46);">'someFile.txt'</span>);
writer.next(<span class="hljs-string" style="color:rgb(166,226,46);">'hello'</span>);
writer.next(<span class="hljs-string" style="color:rgb(166,226,46);">'world'</span>);
<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> writer.return();
</code></pre> 
       <h3 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;">for await...of</h3> 
       <p>前面介绍过,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for...of</code>循环用于遍历同步的 Iterator 接口。新引入的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for await...of</code>循环,则是用于遍历异步的 Iterator 接口。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> x <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> createAsyncIterable([<span class="hljs-string" style="color:rgb(166,226,46);">'a'</span>, <span class="hljs-string" style="color:rgb(166,226,46);">'b'</span>])) {
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(x);
  }
}
<span class="hljs-comment" style="color:rgb(117,113,94);">// a</span>
<span class="hljs-comment" style="color:rgb(117,113,94);">// b</span>
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">createAsyncIterable()</code>返回一个异步遍历器,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for...of</code>循环自动调用这个遍历器的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法,会得到一个 Promise 对象。<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>用来处理这个 Promise 对象,一旦<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">resolve</code>,就把得到的值(<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">x</code>)传入<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for...of</code>的循环体。</p> 
       <p><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for await...of</code>循环的一个用途,是部署了 asyncIterable 操作的异步接口,可以直接放入这个循环。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> body = <span class="hljs-string" style="color:rgb(166,226,46);">''</span>;

<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span>(<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> data <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> req) body += data;
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> parsed = <span class="hljs-built_in" style="color:rgb(249,38,114);">JSON</span>.parse(body);
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-string" style="color:rgb(166,226,46);">'got'</span>, parsed);
}
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">req</code>是一个 asyncIterable 对象,用来异步读取数据。可以看到,使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for await...of</code>循环以后,代码会非常简洁。</p> 
       <p>如果<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法返回的 Promise 对象被<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reject</code>,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for await...of</code>就会报错,要用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">try...catch</code>捕捉。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> x <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> createRejectingIterable()) {
      <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(x);
    }
  } <span class="hljs-keyword" style="color:rgb(102,217,239);">catch</span> (e) {
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.error(e);
  }
}
</code></pre> 
       <p>注意,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for await...of</code>循环也可以用于同步遍历器。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;">(<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> x <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> [<span class="hljs-string" style="color:rgb(166,226,46);">'a'</span>, <span class="hljs-string" style="color:rgb(166,226,46);">'b'</span>]) {
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(x);
  }
})();
<span class="hljs-comment" style="color:rgb(117,113,94);">// a</span>
<span class="hljs-comment" style="color:rgb(117,113,94);">// b</span>
</code></pre> 
       <h3 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;">异步 Generator 函数</h3> 
       <p>就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。</p> 
       <p>在语法上,异步 Generator 函数就是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">async</code>函数与 Generator 函数的结合。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">gen</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-string" style="color:rgb(166,226,46);">'hello'</span>;
}
<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> genObj = gen();
genObj.next().then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">x</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(x));
<span class="hljs-comment" style="color:rgb(117,113,94);">// { value: 'hello', done: false }</span>
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">gen</code>是一个异步 Generator 函数,执行后返回一个异步 Iterator 对象。对该对象调用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法,返回一个 Promise 对象。</p> 
       <p>异步遍历器的设计目的之一,就是 Generator 函数处理同步操作和异步操作时,能够使用同一套接口。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-comment" style="color:rgb(117,113,94);">// 同步 Generator 函数</span>
<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">map</span>(<span class="hljs-params" style="color:rgb(245,135,31);">iterable, func</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> iter = iterable[<span class="hljs-built_in" style="color:rgb(249,38,114);">Symbol</span>.iterator]();
  <span class="hljs-keyword" style="color:rgb(102,217,239);">while</span> (<span class="hljs-literal" style="color:rgb(174,129,255);">true</span>) {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> {value, done} = iter.next();
    <span class="hljs-keyword" style="color:rgb(102,217,239);">if</span> (done) <span class="hljs-keyword" style="color:rgb(102,217,239);">break</span>;
    <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> func(value);
  }
}

<span class="hljs-comment" style="color:rgb(117,113,94);">// 异步 Generator 函数</span>
<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">map</span>(<span class="hljs-params" style="color:rgb(245,135,31);">iterable, func</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> iter = iterable[<span class="hljs-built_in" style="color:rgb(249,38,114);">Symbol</span>.asyncIterator]();
  <span class="hljs-keyword" style="color:rgb(102,217,239);">while</span> (<span class="hljs-literal" style="color:rgb(174,129,255);">true</span>) {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> {value, done} = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> iter.next();
    <span class="hljs-keyword" style="color:rgb(102,217,239);">if</span> (done) <span class="hljs-keyword" style="color:rgb(102,217,239);">break</span>;
    <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> func(value);
  }
}
</code></pre> 
       <p>上面代码中,可以看到有了异步遍历器以后,同步 Generator 函数和异步 Generator 函数的写法基本上是一致的。</p> 
       <p>下面是另一个异步 Generator 函数的例子。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">readLines</span>(<span class="hljs-params" style="color:rgb(245,135,31);">path</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">let</span> file = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> fileOpen(path);

  <span class="hljs-keyword" style="color:rgb(102,217,239);">try</span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">while</span> (!file.EOF) {
      <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> file.readLine();
    }
  } <span class="hljs-keyword" style="color:rgb(102,217,239);">finally</span> {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> file.close();
  }
}
</code></pre> 
       <p>上面代码中,异步操作前面使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>关键字标明,即<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>后面的操作,应该返回 Promise 对象。凡是使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">yield</code>关键字的地方,就是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法的停下来的地方,它后面的表达式的值(即<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await file.readLine()</code>的值),会作为<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next()</code>返回对象的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">value</code>属性,这一点是与同步 Generator 函数一致的。</p> 
       <p>异步 Generator 函数内部,能够同时使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>和<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">yield</code>命令。可以这样理解,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令用于将外部操作产生的值输入函数内部,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">yield</code>命令用于将函数内部的值输出。</p> 
       <p>上面代码定义的异步 Generator 函数的用法如下。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;">(<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> line <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> readLines(filePath)) {
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(line);
  }
})()
</code></pre> 
       <p>异步 Generator 函数可以与<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for await...of</code>循环结合起来使用。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">prefixLines</span>(<span class="hljs-params" style="color:rgb(245,135,31);">asyncIterable</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> line <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> asyncIterable) {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-string" style="color:rgb(166,226,46);">'> '</span> + line;
  }
}
</code></pre> 
       <p>异步 Generator 函数的返回值是一个异步 Iterator,即每次调用它的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法,会返回一个 Promise 对象,也就是说,跟在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">yield</code>命令后面的,应该是一个 Promise 对象。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">asyncGenerator</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-string" style="color:rgb(166,226,46);">'Start'</span>);
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> result = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> doSomethingAsync(); <span class="hljs-comment" style="color:rgb(117,113,94);">// (A)</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-string" style="color:rgb(166,226,46);">'Result: '</span>+ result; <span class="hljs-comment" style="color:rgb(117,113,94);">// (B)</span>
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(<span class="hljs-string" style="color:rgb(166,226,46);">'Done'</span>);
}

<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> ag = asyncGenerator();
ag.next().then({value, done} => {
  <span class="hljs-comment" style="color:rgb(117,113,94);">// ...</span>
})
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">ag</code>是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">asyncGenerator</code>函数返回的异步 Iterator 对象。调用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">ag.next()</code>以后,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">asyncGenerator</code>函数内部的执行顺序如下。</p> 
       <ol class="wiz-list-level1"> 
        <li>打印出<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">Start</code>。</li> 
        <li><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">await</code>命令返回一个 Promise 对象,但是程序不会停在这里,继续往下执行。</li> 
        <li>程序在<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">B</code>处暂停执行,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">yield</code>命令立刻返回一个 Promise 对象,该对象就是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">ag.next()</code>的返回值。</li> 
        <li><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">A</code>处<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">await</code>命令后面的那个 Promise 对象 resolved,产生的值放入<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">result</code>变量。</li> 
        <li><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">B</code>处的 Promise 对象 resolved,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">then</code>方法指定的回调函数开始执行,该函数的参数是一个对象,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">value</code>的值是表达式<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">'Result: ' + result</code>的值,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">done</code>属性的值是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;">false</code>。</li> 
       </ol> 
       <p>A 和 B 两行的作用类似于下面的代码。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">new</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Promise</span>(<span class="hljs-function">(<span class="hljs-params" style="color:rgb(245,135,31);">resolve, reject</span>) =></span> {
  doSomethingAsync()
  .then(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">result</span> =></span> {
     resolve({
       <span class="hljs-attr">value</span>: <span class="hljs-string" style="color:rgb(166,226,46);">'Result: '</span>+result,
       <span class="hljs-attr">done</span>: <span class="hljs-literal" style="color:rgb(174,129,255);">false</span>,
     });
  });
});
</code></pre> 
       <p>如果异步 Generator 函数抛出错误,会被 Promise 对象<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">reject</code>,然后抛出的错误被<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">catch</code>方法捕获。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">asyncGenerator</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">throw</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">new</span> <span class="hljs-built_in" style="color:rgb(249,38,114);">Error</span>(<span class="hljs-string" style="color:rgb(166,226,46);">'Problem!'</span>);
}

asyncGenerator()
.next()
.catch(<span class="hljs-function"><span class="hljs-params" style="color:rgb(245,135,31);">err</span> =></span> <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(err)); <span class="hljs-comment" style="color:rgb(117,113,94);">// Error: Problem!</span>
</code></pre> 
       <p>注意,普通的 async 函数返回的是一个 Promise 对象,而异步 Generator 函数返回的是一个异步 Iterator 对象。可以这样理解,async 函数和异步 Generator 函数,是封装异步操作的两种方法,都用来达到同一种目的。区别在于,前者自带执行器,后者通过<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for await...of</code>执行,或者自己编写执行器。下面就是一个异步 Generator 函数的执行器。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">takeAsync</span>(<span class="hljs-params" style="color:rgb(245,135,31);">asyncIterable, count = Infinity</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> result = [];
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> iterator = asyncIterable[<span class="hljs-built_in" style="color:rgb(249,38,114);">Symbol</span>.asyncIterator]();
  <span class="hljs-keyword" style="color:rgb(102,217,239);">while</span> (result.length < count) {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> {value, done} = <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> iterator.next();
    <span class="hljs-keyword" style="color:rgb(102,217,239);">if</span> (done) <span class="hljs-keyword" style="color:rgb(102,217,239);">break</span>;
    result.push(value);
  }
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> result;
}
</code></pre> 
       <p>上面代码中,异步 Generator 函数产生的异步遍历器,会通过<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">while</code>循环自动执行,每当<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await iterator.next()</code>完成,就会进入下一轮循环。一旦<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">done</code>属性变为<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">true</code>,就会跳出循环,异步遍历器执行结束。</p> 
       <p>下面是这个自动执行器的一个使用实例。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> <span class="hljs-title" style="color:rgb(166,226,46);">f</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">gen</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
    <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-string" style="color:rgb(166,226,46);">'a'</span>;
    <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-string" style="color:rgb(166,226,46);">'b'</span>;
    <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-string" style="color:rgb(166,226,46);">'c'</span>;
  }

  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> takeAsync(gen());
}

f().then(<span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);">result</span>) </span>{
  <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(result); <span class="hljs-comment" style="color:rgb(117,113,94);">// ['a', 'b', 'c']</span>
})
</code></pre> 
       <p>异步 Generator 函数出现以后,JavaScript 就有了四种函数形式:普通函数、async 函数、Generator 函数和异步 Generator 函数。请注意区分每种函数的不同之处。基本上,如果是一系列按照顺序执行的异步操作(比如读取文件,然后写入新内容,再存入硬盘),可以使用 async 函数;如果是一系列产生相同数据结构的异步操作(比如一行一行读取文件),可以使用异步 Generator 函数。</p> 
       <p>异步 Generator 函数也可以通过<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法的参数,接收外部传入的数据。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> writer = openFile(<span class="hljs-string" style="color:rgb(166,226,46);">'someFile.txt'</span>);
writer.next(<span class="hljs-string" style="color:rgb(166,226,46);">'hello'</span>); <span class="hljs-comment" style="color:rgb(117,113,94);">// 立即执行</span>
writer.next(<span class="hljs-string" style="color:rgb(166,226,46);">'world'</span>); <span class="hljs-comment" style="color:rgb(117,113,94);">// 立即执行</span>
<span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> writer.return(); <span class="hljs-comment" style="color:rgb(117,113,94);">// 等待写入结束</span>
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">openFile</code>是一个异步 Generator 函数。<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法的参数,向该函数内部的操作传入数据。每次<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">next</code>方法都是同步执行的,最后的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>命令用于等待整个写入操作结束。</p> 
       <p>最后,同步的数据结构,也可以使用异步 Generator 函数。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">createAsyncIterable</span>(<span class="hljs-params" style="color:rgb(245,135,31);">syncIterable</span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> elem <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> syncIterable) {
    <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> elem;
  }
}
</code></pre> 
       <p>上面代码中,由于没有异步操作,所以也就没有使用<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">await</code>关键字。</p> 
       <h3 class="line" style="font-family:'Microsoft Yahei', 'Helvetica Neue', Arial, Helvetica, sans-serif;font-weight:200;">yield* 语句</h3> 
       <p><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">yield*</code>语句也可以跟一个异步遍历器。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;"><span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">gen1</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-string" style="color:rgb(166,226,46);">'a'</span>;
  <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span> <span class="hljs-string" style="color:rgb(166,226,46);">'b'</span>;
  <span class="hljs-keyword" style="color:rgb(102,217,239);">return</span> <span class="hljs-number" style="color:rgb(174,129,255);">2</span>;
}

<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span>* <span class="hljs-title" style="color:rgb(166,226,46);">gen2</span>(<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-comment" style="color:rgb(117,113,94);">// result 最终会等于 2</span>
  <span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> result = <span class="hljs-keyword" style="color:rgb(102,217,239);">yield</span>* gen1();
}
</code></pre> 
       <p>上面代码中,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">gen2</code>函数里面的<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">result</code>变量,最后的值是<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">2</code>。</p> 
       <p>与同步 Generator 函数一样,<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">for await...of</code>循环会展开<code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(249,242,244);color:rgb(199,37,78);">yield*</code>。</p> 
       <pre style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;background:rgb(17,17,17);border:0px;color:rgb(166,226,46);"><code style="font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace;border:0px;">(<span class="hljs-keyword" style="color:rgb(102,217,239);">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color:rgb(102,217,239);">function</span> (<span class="hljs-params" style="color:rgb(245,135,31);"></span>) </span>{
  <span class="hljs-keyword" style="color:rgb(102,217,239);">for</span> <span class="hljs-keyword" style="color:rgb(102,217,239);">await</span> (<span class="hljs-keyword" style="color:rgb(102,217,239);">const</span> x <span class="hljs-keyword" style="color:rgb(102,217,239);">of</span> gen2()) {
    <span class="hljs-built_in" style="color:rgb(249,38,114);">console</span>.log(x);
  }
})();
<span class="hljs-comment" style="color:rgb(117,113,94);">// a</span>
<span class="hljs-comment" style="color:rgb(117,113,94);">// b</span></code></pre> 
      </div> 
      <div style="color:#808080;"> 
       <br> 
      </div> 
     </div> 
    </div> 
    <br> 
   </div> 
   <div id="wiz-table-col-line"></div> 
   <div id="wiz-table-row-line"></div> 
   <div id="wiz-table-range-border_start"> 
    <div id="wiz-table-range-border_start_top"></div> 
    <div id="wiz-table-range-border_start_right"></div> 
    <div id="wiz-table-range-border_start_bottom"></div> 
    <div id="wiz-table-range-border_start_left"></div> 
    <div id="wiz-table-range-border_start_dot"></div> 
   </div> 
   <div id="wiz-table-range-border_range"> 
    <div id="wiz-table-range-border_range_top"></div> 
    <div id="wiz-table-range-border_range_right"></div> 
    <div id="wiz-table-range-border_range_bottom"></div> 
    <div id="wiz-table-range-border_range_left"></div> 
    <div id="wiz-table-range-border_range_dot"></div> 
   </div> 
  </div> 
  <p>转载于:https://www.cnblogs.com/ChenChunChang/p/8296339.html</p> 
 </div> 
</div>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1297223258864689152"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(17.async 函数)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1835512542735200256.htm"
                           title="C语言宏函数" target="_blank">C语言宏函数</a>
                        <span class="text-muted">南林yan</span>
<a class="tag" taget="_blank" href="/search/C%E8%AF%AD%E8%A8%80/1.htm">C语言</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a>
                        <div>一、什么是宏函数?通过宏定义的函数是宏函数。如下,编译器在预处理阶段会将Add(x,y)替换为((x)*(y))#defineAdd(x,y)((x)*(y))#defineAdd(x,y)((x)*(y))intmain(){inta=10;intb=20;intd=10;intc=Add(a+d,b)*2;cout<<c<<endl;//800return0;}二、为什么要使用宏函数使用宏函数</div>
                    </li>
                    <li><a href="/article/1835511911769272320.htm"
                           title="C语言如何定义宏函数?" target="_blank">C语言如何定义宏函数?</a>
                        <span class="text-muted">小九格物</span>
<a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a>
                        <div>在C语言中,宏函数是通过预处理器定义的,它在编译之前替换代码中的宏调用。宏函数可以模拟函数的行为,但它们不是真正的函数,因为它们在编译时不会进行类型检查,也不会分配存储空间。宏函数的定义通常使用#define指令,后面跟着宏的名称和参数列表,以及宏展开后的代码。宏函数的定义方式:1.基本宏函数:这是最简单的宏函数形式,它直接定义一个表达式。#defineSQUARE(x)((x)*(x))2.带参</div>
                    </li>
                    <li><a href="/article/1835507248395284480.htm"
                           title="【一起学Rust | 设计模式】习惯语法——使用借用类型作为参数、格式化拼接字符串、构造函数" target="_blank">【一起学Rust | 设计模式】习惯语法——使用借用类型作为参数、格式化拼接字符串、构造函数</a>
                        <span class="text-muted">广龙宇</span>
<a class="tag" taget="_blank" href="/search/%E4%B8%80%E8%B5%B7%E5%AD%A6Rust/1.htm">一起学Rust</a><a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Rust%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">Rust设计模式</a><a class="tag" taget="_blank" href="/search/rust/1.htm">rust</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、使用借用类型作为参数二、格式化拼接字符串三、使用构造函数总结前言Rust不是传统的面向对象编程语言,它的所有特性,使其独一无二。因此,学习特定于Rust的设计模式是必要的。本系列文章为作者学习《Rust设计模式》的学习笔记以及自己的见解。因此,本系列文章的结构也与此书的结构相同(后续可能会调成结构),基本上分为三个部分</div>
                    </li>
                    <li><a href="/article/1835506616682770432.htm"
                           title="每日一题——第八十四题" target="_blank">每日一题——第八十四题</a>
                        <span class="text-muted">互联网打工人no1</span>
<a class="tag" taget="_blank" href="/search/C%E8%AF%AD%E8%A8%80%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E6%AF%8F%E6%97%A5%E4%B8%80%E7%BB%83/1.htm">C语言程序设计每日一练</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a>
                        <div>题目:编写函数1、输入10个职工的姓名和职工号2、按照职工由大到小顺序排列,姓名顺序也随之调整3、要求输入一个职工号,用折半查找法找出该职工的姓名#define_CRT_SECURE_NO_WARNINGS#include#include#defineMAX_EMPLOYEES10typedefstruct{intid;charname[50];}Empolyee;voidinputEmploye</div>
                    </li>
                    <li><a href="/article/1835504217729626112.htm"
                           title="Python教程:一文了解使用Python处理XPath" target="_blank">Python教程:一文了解使用Python处理XPath</a>
                        <span class="text-muted">旦莫</span>
<a class="tag" taget="_blank" href="/search/Python%E8%BF%9B%E9%98%B6/1.htm">Python进阶</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>目录1.环境准备1.1安装lxml1.2验证安装2.XPath基础2.1什么是XPath?2.2XPath语法2.3示例XML文档3.使用lxml解析XML3.1解析XML文档3.2查看解析结果4.XPath查询4.1基本路径查询4.2使用属性查询4.3查询多个节点5.XPath的高级用法5.1使用逻辑运算符5.2使用函数6.实战案例6.1从网页抓取数据6.1.1安装Requests库6.1.2代</div>
                    </li>
                    <li><a href="/article/1835495770502033408.htm"
                           title="Day17笔记-高阶函数" target="_blank">Day17笔记-高阶函数</a>
                        <span class="text-muted">~在杰难逃~</span>
<a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/pycharm/1.htm">pycharm</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/1.htm">数据分析</a>
                        <div>高阶函数【重点掌握】函数的本质:函数是一个变量,函数名是一个变量名,一个函数可以作为另一个函数的参数或返回值使用如果A函数作为B函数的参数,B函数调用完成之后,会得到一个结果,则B函数被称为高阶函数常用的高阶函数:map(),reduce(),filter(),sorted()1.map()map(func,iterable),返回值是一个iterator【容器,迭代器】func:函数iterab</div>
                    </li>
                    <li><a href="/article/1835493626688401408.htm"
                           title="Python快速入门 —— 第三节:类与对象" target="_blank">Python快速入门 —— 第三节:类与对象</a>
                        <span class="text-muted">孤华暗香</span>
<a class="tag" taget="_blank" href="/search/Python%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/1.htm">Python快速入门</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>第三节:类与对象目标:了解面向对象编程的基础概念,并学会如何定义类和创建对象。内容:类与对象:定义类:class关键字。类的构造函数:__init__()。类的属性和方法。对象的创建与使用。示例:classStudent:def__init__(self,name,age,major):self.name&#</div>
                    </li>
                    <li><a href="/article/1835490218845761536.htm"
                           title="Python爬虫解析工具之xpath使用详解" target="_blank">Python爬虫解析工具之xpath使用详解</a>
                        <span class="text-muted">eqa11</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>文章目录Python爬虫解析工具之xpath使用详解一、引言二、环境准备1、插件安装2、依赖库安装三、xpath语法详解1、路径表达式2、通配符3、谓语4、常用函数四、xpath在Python代码中的使用1、文档树的创建2、使用xpath表达式3、获取元素内容和属性五、总结Python爬虫解析工具之xpath使用详解一、引言在Python爬虫开发中,数据提取是一个至关重要的环节。xpath作为一门</div>
                    </li>
                    <li><a href="/article/1835489588169240576.htm"
                           title="ARM驱动学习之5 LEDS驱动" target="_blank">ARM驱动学习之5 LEDS驱动</a>
                        <span class="text-muted">JT灬新一</span>
<a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F/1.htm">嵌入式</a><a class="tag" taget="_blank" href="/search/C/1.htm">C</a><a class="tag" taget="_blank" href="/search/%E5%BA%95%E5%B1%82/1.htm">底层</a><a class="tag" taget="_blank" href="/search/arm%E5%BC%80%E5%8F%91/1.htm">arm开发</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a>
                        <div>ARM驱动学习之5LEDS驱动知识点:•linuxGPIO申请函数和赋值函数–gpio_request–gpio_set_value•三星平台配置GPIO函数–s3c_gpio_cfgpin•GPIO配置输出模式的宏变量–S3C_GPIO_OUTPUT注意点:DRIVER_NAME和DEVICE_NAME匹配。实现步骤:1.加入需要的头文件://Linux平台的gpio头文件#include//三</div>
                    </li>
                    <li><a href="/article/1835489208152715264.htm"
                           title="Rust基础知识" target="_blank">Rust基础知识</a>
                        <span class="text-muted">GRKF15</span>
<a class="tag" taget="_blank" href="/search/rust/1.htm">rust</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a>
                        <div>1.Rust语言简介1.1基础语法变量声明:let关键字用于声明变量,可以指定或不指定类型,如leta=10;和letmutc=30i32;。函数定义:使用fn关键字定义函数,并指定参数类型及返回类型,如fnadd(i:i32,j:i32)->i32{i+j}。控制流:包括if、else等,控制语句后需要使用;来结束语句。1.2数据类型整数类型:i8、i16、i32、i64、i128,以及无符号的</div>
                    </li>
                    <li><a href="/article/1835475216491442176.htm"
                           title="STM32中的计时与延时" target="_blank">STM32中的计时与延时</a>
                        <span class="text-muted">lupinjia</span>
<a class="tag" taget="_blank" href="/search/STM32/1.htm">STM32</a><a class="tag" taget="_blank" href="/search/stm32/1.htm">stm32</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a>
                        <div>前言在裸机开发中,延时作为一种规定循环周期的方式经常被使用,其中尤以HAL库官方提供的HAL_Delay为甚。刚入门的小白可能会觉得既然有官方提供的延时函数,而且精度也还挺好,为什么不用呢?实际上HAL_Delay中有不少坑,而这些也只是HAL库中无数坑的其中一些。想从坑里跳出来还是得加强外设原理的学习和理解,切不可只依赖HAL库。除了延时之外,我们在开发中有时也会想要确定某段程序的耗时,这就需要</div>
                    </li>
                    <li><a href="/article/1835462484912336896.htm"
                           title="python多线程程序设计 之一" target="_blank">python多线程程序设计 之一</a>
                        <span class="text-muted">IT_Beijing_BIT</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E8%AF%AD%E8%A8%80/1.htm">程序设计语言</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>python多线程程序设计之一全局解释器锁线程APIsthreading.active_count()threading.current_thread()threading.excepthook(args,/)threading.get_native_id()threading.main_thread()threading.stack_size([size])线程对象成员函数构造器start/ru</div>
                    </li>
                    <li><a href="/article/1835448619277316096.htm"
                           title="Android应用性能优化" target="_blank">Android应用性能优化</a>
                        <span class="text-muted">轻口味</span>
<a class="tag" taget="_blank" href="/search/Android/1.htm">Android</a>
                        <div>Android手机由于其本身的后台机制和硬件特点,性能上一直被诟病,所以软件开发者对软件本身的性能优化就显得尤为重要;本文将对Android开发过程中性能优化的各个方面做一个回顾与总结。Cache优化ListView缓存:ListView中有一个回收器,Item滑出界面的时候View会回收到这里,需要显示新的Item的时候,就尽量重用回收器里面的View;每次在getView函数中inflate新</div>
                    </li>
                    <li><a href="/article/1835447606348705792.htm"
                           title="C++ lambda闭包消除类成员变量" target="_blank">C++ lambda闭包消除类成员变量</a>
                        <span class="text-muted">barbyQAQ</span>
<a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>原文链接:https://blog.csdn.net/qq_51470638/article/details/142151502一、背景在面向对象编程时,常常要添加类成员变量。然而类成员一旦多了之后,也会带来干扰。拿到一个类,一看成员变量好几十个,就问你怕不怕?二、解决思路可以借助函数式编程思想,来消除一些不必要的类成员变量。三、实例举个例子:classClassA{public:...intfu</div>
                    </li>
                    <li><a href="/article/1835440294980579328.htm"
                           title="C++八股" target="_blank">C++八股</a>
                        <span class="text-muted">Petrichorzncu</span>
<a class="tag" taget="_blank" href="/search/%E5%85%AB%E8%82%A1%E6%80%BB%E7%BB%93/1.htm">八股总结</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>这里写目录标题C++内存管理C++的构造函数,复制构造函数,和析构函数深复制与浅复制:构造函数和析构函数哪个能写成虚函数,为什么?C++数据结构内存排列结构体和类占用的内存:==虚函数和虚表的原理==虚函数虚表(Vtable)虚函数和虚表的实现细节==内存泄漏==指针的工作原理函数的传值和传址new和delete与malloc和freeC++内存区域划分C++11新特性C++常见新特性==智能指针</div>
                    </li>
                    <li><a href="/article/1835436642270277632.htm"
                           title="【Python搞定车载自动化测试】——Python实现车载以太网DoIP刷写(含Python源码)" target="_blank">【Python搞定车载自动化测试】——Python实现车载以太网DoIP刷写(含Python源码)</a>
                        <span class="text-muted">疯狂的机器人</span>
<a class="tag" taget="_blank" href="/search/Python%E6%90%9E%E5%AE%9A%E8%BD%A6%E8%BD%BD%E8%87%AA%E5%8A%A8%E5%8C%96/1.htm">Python搞定车载自动化</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/DoIP/1.htm">DoIP</a><a class="tag" taget="_blank" href="/search/UDS/1.htm">UDS</a><a class="tag" taget="_blank" href="/search/ISO/1.htm">ISO</a><a class="tag" taget="_blank" href="/search/14229/1.htm">14229</a><a class="tag" taget="_blank" href="/search/1SO/1.htm">1SO</a><a class="tag" taget="_blank" href="/search/13400/1.htm">13400</a><a class="tag" taget="_blank" href="/search/Bootloader/1.htm">Bootloader</a><a class="tag" taget="_blank" href="/search/tcp%2Fip/1.htm">tcp/ip</a>
                        <div>系列文章目录【Python搞定车载自动化测试】系列文章目录汇总文章目录系列文章目录前言一、环境搭建1.软件环境2.硬件环境二、目录结构三、源码展示1.DoIP诊断基础函数方法2.DoIP诊断业务函数方法3.27服务安全解锁4.DoIP自动化刷写四、测试日志1.测试日志五、完整源码链接前言随着智能电动汽车行业的发展,汽车=智能终端+四个轮子,各家车企都推出了各自的OTA升级方案,本章节主要介绍如何使</div>
                    </li>
                    <li><a href="/article/1835435758844997632.htm"
                           title="【2022 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级 C++语言试题及解析】" target="_blank">【2022 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级 C++语言试题及解析】</a>
                        <span class="text-muted">汉子萌萌哒</span>
<a class="tag" taget="_blank" href="/search/CCF/1.htm">CCF</a><a class="tag" taget="_blank" href="/search/noi/1.htm">noi</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a>
                        <div>一、单项选择题(共15题,每题2分,共计30分;每题有且仅有一个正确选项)1.以下哪种功能没有涉及C++语言的面向对象特性支持:()。A.C++中调用printf函数B.C++中调用用户定义的类成员函数C.C++中构造一个class或structD.C++中构造来源于同一基类的多个派生类题目解析【解析】正确答案:AC++基础知识,面向对象和类有关,类又涉及父类、子类、继承、派生等关系,printf</div>
                    </li>
                    <li><a href="/article/1835434623782449152.htm"
                           title="matlab delsat = setdiff(1:69,unique(Eph(30,:))); 语句含义" target="_blank">matlab delsat = setdiff(1:69,unique(Eph(30,:))); 语句含义</a>
                        <span class="text-muted">黄卷青灯77</span>
<a class="tag" taget="_blank" href="/search/matlab/1.htm">matlab</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/setdiff/1.htm">setdiff</a>
                        <div>这行MATLAB代码用于计算在范围1:69中不包含在Eph矩阵第30行的唯一值集合中的所有元素。具体解释如下:delsat=setdiff(1:69,unique(Eph(30,:)));解释Eph(30,:)Eph(30,:)提取矩阵Eph的第30行的所有列元素。这是一个行向量,包含了第30行的所有值。unique(Eph(30,:))unique函数返回Eph(30,:)中的唯一元素。这意味着</div>
                    </li>
                    <li><a href="/article/1835432106029838336.htm"
                           title="【RabbitMQ 项目】服务端:数据管理模块之绑定管理" target="_blank">【RabbitMQ 项目】服务端:数据管理模块之绑定管理</a>
                        <span class="text-muted">月夜星辉雪</span>
<a class="tag" taget="_blank" href="/search/rabbitmq/1.htm">rabbitmq</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a>
                        <div>文章目录一.编写思路二.代码实践一.编写思路定义绑定信息类交换机名称队列名称绑定关键字:交换机的路由交换算法中会用到没有是否持久化的标志,因为绑定是否持久化取决于交换机和队列是否持久化,只有它们都持久化时绑定才需要持久化。绑定就好像一根绳子,两端连接着交换机和队列,当一方不存在,它就没有存在的必要了定义绑定持久化类构造函数:如果数据库文件不存在则创建,打开数据库,创建binding_table插入</div>
                    </li>
                    <li><a href="/article/1835429075590672384.htm"
                           title="粒子群优化 (PSO) 在三维正弦波函数中的应用" target="_blank">粒子群优化 (PSO) 在三维正弦波函数中的应用</a>
                        <span class="text-muted">subject625Ruben</span>
<a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/matlab/1.htm">matlab</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>在这篇博客中,我们将展示如何使用粒子群优化(PSO)算法求解三维正弦波函数,并通过增加正弦波扰动,使优化过程更加复杂和有趣。本文将介绍目标函数的定义、PSO参数设置以及算法执行的详细过程,并展示搜索空间中的动态过程和收敛曲线。1.目标函数定义我们使用的目标函数是一个三维正弦波函数,定义如下:objectiveFunc=@(x)sin(sqrt(x(1).^2+x(2).^2))+0.5*sin(5</div>
                    </li>
                    <li><a href="/article/1835419870565593088.htm"
                           title="c++ 内存处理函数" target="_blank">c++ 内存处理函数</a>
                        <span class="text-muted">heeheeai</span>
<a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>在C语言的头文件中,memcpy和memmove函数都用于复制内存块,但它们在处理内存重叠方面存在关键区别:内存重叠:memcpy函数不保证在源内存和目标内存区域重叠时能够正确复制数据。如果内存区域重叠,memcpy的行为是未定义的,可能会导致数据损坏或程序崩溃。memmove函数能够安全地处理源内存和目标内存区域重叠的情况。它会确保在复制过程中不会覆盖尚未复制的数据,从而保证数据的完整性。效率:</div>
                    </li>
                    <li><a href="/article/1835414702142877696.htm"
                           title="Python编程 - 函数进阶" target="_blank">Python编程 - 函数进阶</a>
                        <span class="text-muted">易辰君</span>
<a class="tag" taget="_blank" href="/search/Python%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B/1.htm">Python核心编程</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>目录前言一、函数参数的高级用法(一)缺省参数(二)命名参数(三)不定长参数二、拆包(一)函数返回值拆包(二)通过星号拆包(三)总结三、匿名函数(一)函数定义(二)使用匿名函数四、递归函数(一)简介(二)基本结构(三)简单示例(四)优缺点总结前言上篇文章主要了解了函数基础,如何定义函数,函数种类以及局部变量和全局变量的差异等,接下来就讲解python函数较为进阶的知识点,若有任何想法欢迎一起沟通讨论</div>
                    </li>
                    <li><a href="/article/1835414575906910208.htm"
                           title="Python编程 - 初识面向对象" target="_blank">Python编程 - 初识面向对象</a>
                        <span class="text-muted">易辰君</span>
<a class="tag" taget="_blank" href="/search/Python%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B/1.htm">Python核心编程</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>目录前言一、面向对象二、类和对象(一)类简介定义类(二)对象简介创建对象(三)总结三、实例属性和实例方法(一)实例属性创建的基本语法使用示例(二)实例方法定义实例方法的基本语法调用示例方法的示例(三)总结四、类中的self(一)基本概念(二)作用访问实例属性调用其他实例方法在构造函数中初始化对象(三)总结五、__init__方法(一)__init__方法的特点(二)基本语法(三)示例(四)总结前言</div>
                    </li>
                    <li><a href="/article/1835412178648264704.htm"
                           title="[实践应用] 深度学习之优化器" target="_blank">[实践应用] 深度学习之优化器</a>
                        <span class="text-muted">YuanDaima2048</span>
<a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8/1.htm">工具使用</a><a class="tag" taget="_blank" href="/search/pytorch/1.htm">pytorch</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E4%BC%98%E5%8C%96%E5%99%A8/1.htm">优化器</a>
                        <div>文章总览:YuanDaiMa2048博客文章总览深度学习之优化器1.随机梯度下降(SGD)2.动量优化(Momentum)3.自适应梯度(Adagrad)4.自适应矩估计(Adam)5.RMSprop总结其他介绍在深度学习中,优化器用于更新模型的参数,以最小化损失函数。常见的优化函数有很多种,下面是几种主流的优化器及其特点、原理和PyTorch实现:1.随机梯度下降(SGD)原理:随机梯度下降通过</div>
                    </li>
                    <li><a href="/article/1835404369252675584.htm"
                           title="Go语言基础总结" target="_blank">Go语言基础总结</a>
                        <span class="text-muted">Alice_小哪吒</span>
<a class="tag" taget="_blank" href="/search/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">Go学习笔记</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a>
                        <div>一、Go语言结构包声明引入包函数变量语句&表达式注释下面简单给出hello.go文件。packagesrc/*定义包名*/import"fmt"/*引入包*/funchello(){/*函数*/fmt.Println("Hello,World!")/*语句&表达式*/fmt.Println("菜鸟教程:runoob.com")}二、Go语言基础语法Go程序可以由多个标记构成。可以是关键字、标识符、</div>
                    </li>
                    <li><a href="/article/1835402857742954496.htm"
                           title="TC27x启动过程(2)-TC277" target="_blank">TC27x启动过程(2)-TC277</a>
                        <span class="text-muted">赞哥哥s</span>
<a class="tag" taget="_blank" href="/search/TC277%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">TC277学习笔记</a><a class="tag" taget="_blank" href="/search/gnu/1.htm">gnu</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a>
                        <div>接上文,继续学习TC277的启动过程。分析启动函数有关用的寄存器说明,参考文章TC27x寄存器学习目录TC27x寄存器学习start函数分析isync汇编指令(同步指令)dsync汇编指令(同步数据),1清除endinit2设置中断堆栈3启用对系统全局寄存器的写访问4初始化SDA基指针5关闭对系统全局寄存器的写访问6关闭看门狗,恢复Endinit位7初始化CSA8初始化ram,拷贝rom数据到ra</div>
                    </li>
                    <li><a href="/article/1835401975269781504.htm"
                           title="vue render 函数详解 (配参数详解)" target="_blank">vue render 函数详解 (配参数详解)</a>
                        <span class="text-muted">你的眼睛會笑</span>
<a class="tag" taget="_blank" href="/search/vue2/1.htm">vue2</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>vuerender函数详解(配参数详解)在Vue3中,`render`函数被用来代替Vue2中的模板语法。它接收一个h函数(或者是`createElement`函数的别名),并且返回一个虚拟DOM。render函数的语法结构如下:render(h){returnh('div',{class:'container'},'Hello,World!')}在上面的示例中,我们使用h函数创建了一个div元素</div>
                    </li>
                    <li><a href="/article/1835400084305571840.htm"
                           title="python老是报参数未定义_Python函数默认参数常见问题及解决方案" target="_blank">python老是报参数未定义_Python函数默认参数常见问题及解决方案</a>
                        <span class="text-muted">weixin_39935571</span>
<a class="tag" taget="_blank" href="/search/python%E8%80%81%E6%98%AF%E6%8A%A5%E5%8F%82%E6%95%B0%E6%9C%AA%E5%AE%9A%E4%B9%89/1.htm">python老是报参数未定义</a>
                        <div>一、默认参数python为了简化函数的调用,提供了默认参数机制:这样在调用pow函数时,就可以省略最后一个参数不写:在定义有默认参数的函数时,需要注意以下:必选参数必须在前面,默认参数在后;设置何种参数为默认参数?一般来说,将参数值变化小的设置为默认参数。python标准库实践python内建函数:函数签名可以看出,使用print('hellopython')这样的简单调用的打印语句,实际上传入了</div>
                    </li>
                    <li><a href="/article/1835394725914439680.htm"
                           title="3.1 损失函数和优化:损失函数" target="_blank">3.1 损失函数和优化:损失函数</a>
                        <span class="text-muted">做只小考拉</span>

                        <div>用一个函数把W当做输入,然后看一下得分,定量地估计W的好坏,这个函数被称为“损失函数”。损失函数用于度量W的好坏。有了损失函数的概念后,就可以定量的衡量W到底是好还是坏,要找到一种有效的方法来从W的可行域里,找到W取何值时情况最不坏,,这个过程将会是一个优化过程。损失函数L_i定义:通过函数f给出预测的分数和真实的目标(或者说是标签y),可以定量的描述训练样本预测的好不好,最终的损失函数是在整个数</div>
                    </li>
                    <li><a href="/article/1835390752381825024.htm"
                           title="详解“c:/work/src/components/a/b.vue“‘ has no default export报错原因" target="_blank">详解“c:/work/src/components/a/b.vue“‘ has no default export报错原因</a>
                        <span class="text-muted">hw_happy</span>
<a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>前情提要在一个vue文件中需要引入定义的b.vue文件,但是提示b文件没有默认导出,对于vue2文件来说有exportdefault,在中,所有定义的变量、函数和组件都会自动被视为默认导出的组件内容。因此,不需要显式地使用exportdefault来导出组件。但是在我引用这个文件的时候还是提示了这个错误,原来是我的项目使用了ts和vite\webpack,因为TypeScript和Vue的默认导出</div>
                    </li>
                                <li><a href="/article/52.htm"
                                       title="解读Servlet原理篇二---GenericServlet与HttpServlet" target="_blank">解读Servlet原理篇二---GenericServlet与HttpServlet</a>
                                    <span class="text-muted">周凡杨</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/HttpServlet/1.htm">HttpServlet</a><a class="tag" taget="_blank" href="/search/%E6%BA%90%E7%90%86/1.htm">源理</a><a class="tag" taget="_blank" href="/search/GenericService/1.htm">GenericService</a><a class="tag" taget="_blank" href="/search/%E6%BA%90%E7%A0%81/1.htm">源码</a>
                                    <div>在上一篇《解读Servlet原理篇一》中提到,要实现javax.servlet.Servlet接口(即写自己的Servlet应用),你可以写一个继承自javax.servlet.GenericServletr的generic Servlet ,也可以写一个继承自java.servlet.http.HttpServlet的HTTP Servlet(这就是为什么我们自定义的Servlet通常是exte</div>
                                </li>
                                <li><a href="/article/179.htm"
                                       title="MySQL性能优化" target="_blank">MySQL性能优化</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a>
                                    <div>        性能优化是通过某些有效的方法来提高MySQL的运行速度,减少占用的磁盘空间。性能优化包含很多方面,例如优化查询速度,优化更新速度和优化MySQL服务器等。本文介绍方法的主要有: 
        a.优化查询 
        b.优化数据库结构 
  </div>
                                </li>
                                <li><a href="/article/306.htm"
                                       title="ThreadPool定时重试" target="_blank">ThreadPool定时重试</a>
                                    <span class="text-muted">dai_lm</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/ThreadPool/1.htm">ThreadPool</a><a class="tag" taget="_blank" href="/search/thread/1.htm">thread</a><a class="tag" taget="_blank" href="/search/timer/1.htm">timer</a><a class="tag" taget="_blank" href="/search/timertask/1.htm">timertask</a>
                                    <div>项目需要当某事件触发时,执行http请求任务,失败时需要有重试机制,并根据失败次数的增加,重试间隔也相应增加,任务可能并发。 
由于是耗时任务,首先考虑的就是用线程来实现,并且为了节约资源,因而选择线程池。 
为了解决不定间隔的重试,选择Timer和TimerTask来完成 
 
 

package threadpool;

public class ThreadPoolTest {

</div>
                                </li>
                                <li><a href="/article/433.htm"
                                       title="Oracle 查看数据库的连接情况" target="_blank">Oracle 查看数据库的连接情况</a>
                                    <span class="text-muted">周凡杨</span>
<a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/oracle+%E8%BF%9E%E6%8E%A5/1.htm">oracle 连接</a>
                                    <div>首先要说的是,不同版本数据库提供的系统表会有不同,你可以根据数据字典查看该版本数据库所提供的表。 
 
select * from dict where table_name like '%SESSION%'; 
就可以查出一些表,然后根据这些表就可以获得会话信息 
 
select sid,serial#,status,username,schemaname,osuser,terminal,ma</div>
                                </li>
                                <li><a href="/article/560.htm"
                                       title="类的继承" target="_blank">类的继承</a>
                                    <span class="text-muted">朱辉辉33</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>类的继承可以提高代码的重用行,减少冗余代码;还能提高代码的扩展性。Java继承的关键字是extends 
格式:public class 类名(子类)extends 类名(父类){ } 
子类可以继承到父类所有的属性和普通方法,但不能继承构造方法。且子类可以直接使用父类的public和 
protected属性,但要使用private属性仍需通过调用。 
子类的方法可以重写,但必须和父类的返回值类</div>
                                </li>
                                <li><a href="/article/687.htm"
                                       title="android 悬浮窗特效" target="_blank">android 悬浮窗特效</a>
                                    <span class="text-muted">肆无忌惮_</span>
<a class="tag" taget="_blank" href="/search/android/1.htm">android</a>
                                    <div>最近在开发项目的时候需要做一个悬浮层的动画,类似于支付宝掉钱动画。但是区别在于,需求是浮出一个窗口,之后边缩放边位移至屏幕右下角标签处。效果图如下: 
  
一开始考虑用自定义View来做。后来发现开线程让其移动很卡,ListView+动画也没法精确定位到目标点。 
  
后来想利用Dialog的dismiss动画来完成。 
  
自定义一个Dialog后,在styl</div>
                                </li>
                                <li><a href="/article/814.htm"
                                       title="hadoop伪分布式搭建" target="_blank">hadoop伪分布式搭建</a>
                                    <span class="text-muted">林鹤霄</span>
<a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a>
                                    <div>要修改4个文件    1: vim hadoop-env.sh  第九行    2: vim core-site.xml            <configuration>     &n</div>
                                </li>
                                <li><a href="/article/941.htm"
                                       title="gdb调试命令" target="_blank">gdb调试命令</a>
                                    <span class="text-muted">aigo</span>
<a class="tag" taget="_blank" href="/search/gdb/1.htm">gdb</a>
                                    <div>原文:http://blog.csdn.net/hanchaoman/article/details/5517362 
  
一、GDB常用命令简介 
     r run 运行.程序还没有运行前使用   c             cuntinue </div>
                                </li>
                                <li><a href="/article/1068.htm"
                                       title="Socket编程的HelloWorld实例" target="_blank">Socket编程的HelloWorld实例</a>
                                    <span class="text-muted">alleni123</span>
<a class="tag" taget="_blank" href="/search/socket/1.htm">socket</a>
                                    <div>public class Client
{
	
	
	public static void main(String[] args)
	{	
		Client c=new Client();
	 	c.receiveMessage();
	}
	
	public void receiveMessage(){
		Socket s=null;
		
		BufferedRea</div>
                                </li>
                                <li><a href="/article/1195.htm"
                                       title="线程同步和异步" target="_blank">线程同步和异步</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/1.htm">线程同步</a><a class="tag" taget="_blank" href="/search/%E5%BC%82%E6%AD%A5/1.htm">异步</a>
                                    <div>多线程和同步 : 如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。  所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法  
  
多线程和异步:多线程可以做不同的事情,涉及到线程通知 
  
  
&</div>
                                </li>
                                <li><a href="/article/1322.htm"
                                       title="JSP中文乱码分析" target="_blank">JSP中文乱码分析</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jsp/1.htm">jsp</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81/1.htm">中文乱码</a>
                                    <div>        在JSP的开发过程中,经常出现中文乱码的问题。 
        首先了解一下Java中文问题的由来: 
        Java的内核和class文件是基于unicode的,这使Java程序具有良好的跨平台性,但也带来了一些中文乱码问题的麻烦。原因主要有两方面,</div>
                                </li>
                                <li><a href="/article/1449.htm"
                                       title="js实现页面跳转重定向的几种方式" target="_blank">js实现页面跳转重定向的几种方式</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/%E9%87%8D%E5%AE%9A%E5%90%91/1.htm">重定向</a>
                                    <div>        js实现页面跳转重定向有如下几种方式: 
一.window.location.href 
<script language="javascript"type="text/javascript"> 
	window.location.href="http://www.baidu.c</div>
                                </li>
                                <li><a href="/article/1576.htm"
                                       title="【Struts2三】Struts2 Action转发类型" target="_blank">【Struts2三】Struts2 Action转发类型</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/struts2/1.htm">struts2</a>
                                    <div> 在【Struts2一】 Struts Hello World http://bit1129.iteye.com/blog/2109365中配置了一个简单的Action,配置如下 
  
<!DOCTYPE struts PUBLIC  
        "-//Apache Software Foundation//DTD Struts Configurat</div>
                                </li>
                                <li><a href="/article/1703.htm"
                                       title="【HBase十一】Java API操作HBase" target="_blank">【HBase十一】Java API操作HBase</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/hbase/1.htm">hbase</a>
                                    <div>Admin类的主要方法注释: 
  1. 创建表 
 /**
   * Creates a new table. Synchronous operation.
   *
   * @param desc table descriptor for table
   * @throws IllegalArgumentException if the table name is res</div>
                                </li>
                                <li><a href="/article/1830.htm"
                                       title="nginx gzip" target="_blank">nginx gzip</a>
                                    <span class="text-muted">ronin47</span>
<a class="tag" taget="_blank" href="/search/nginx+gzip/1.htm">nginx gzip</a>
                                    <div>Nginx GZip 压缩  
Nginx GZip 模块文档详见:http://wiki.nginx.org/HttpGzipModule 
常用配置片段如下:  
gzip on; gzip_comp_level 2; # 压缩比例,比例越大,压缩时间越长。默认是1 gzip_types text/css text/javascript; # 哪些文件可以被压缩 gzip_disable &q</div>
                                </li>
                                <li><a href="/article/1957.htm"
                                       title="java-7.微软亚院之编程判断俩个链表是否相交 给出俩个单向链表的头指针,比如 h1 , h2 ,判断这俩个链表是否相交" target="_blank">java-7.微软亚院之编程判断俩个链表是否相交 给出俩个单向链表的头指针,比如 h1 , h2 ,判断这俩个链表是否相交</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>

public class LinkListTest {

	/**
	 * we deal with two main missions:
	 * 
	 * A.
	 * 1.we create two joined-List(both have no loop)
	 * 2.whether list1 and list2 join
	 * 3.print the join</div>
                                </li>
                                <li><a href="/article/2084.htm"
                                       title="Spring源码学习-JdbcTemplate batchUpdate批量操作" target="_blank">Spring源码学习-JdbcTemplate batchUpdate批量操作</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                                    <div>Spring JdbcTemplate的batch操作最后还是利用了JDBC提供的方法,Spring只是做了一下改造和封装 
 
JDBC的batch操作: 
 
 


String sql = "INSERT INTO CUSTOMER " +
				  "(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
				</div>
                                </li>
                                <li><a href="/article/2211.htm"
                                       title="[JWFD开源工作流]大规模拓扑矩阵存储结构最新进展" target="_blank">[JWFD开源工作流]大规模拓扑矩阵存储结构最新进展</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C%E6%B5%81/1.htm">工作流</a>
                                    <div>    生成和创建类已经完成,构造一个100万个元素的矩阵模型,存储空间只有11M大,请大家参考我在博客园上面的文档"构造下一代工作流存储结构的尝试",更加相信的设计和代码将陆续推出......... 
 
    竞争对手的能力也很强.......,我相信..你们一定能够先于我们推出大规模拓扑扫描和分析系统的....</div>
                                </li>
                                <li><a href="/article/2338.htm"
                                       title="base64编码和url编码" target="_blank">base64编码和url编码</a>
                                    <span class="text-muted">cuityang</span>
<a class="tag" taget="_blank" href="/search/base64/1.htm">base64</a><a class="tag" taget="_blank" href="/search/url/1.htm">url</a>
                                    <div>import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.io.PrintWriter; 
import java.io.StringWriter; 
import java.io.UnsupportedEncodingException; </div>
                                </li>
                                <li><a href="/article/2465.htm"
                                       title="web应用集群Session保持" target="_blank">web应用集群Session保持</a>
                                    <span class="text-muted">dalan_123</span>
<a class="tag" taget="_blank" href="/search/session/1.htm">session</a>
                                    <div>关于使用 memcached 或redis 存储 session ,以及使用 terracotta 服务器共享。建议使用 redis,不仅仅因为它可以将缓存的内容持久化,还因为它支持的单个对象比较大,而且数据类型丰富,不只是缓存 session,还可以做其他用途,一举几得啊。1、使用 filter 方法存储这种方法比较推荐,因为它的服务器使用范围比较多,不仅限于tomcat ,而且实现的原理比较简</div>
                                </li>
                                <li><a href="/article/2719.htm"
                                       title="Yii 框架里数据库操作详解-[增加、查询、更新、删除的方法 'AR模式']" target="_blank">Yii 框架里数据库操作详解-[增加、查询、更新、删除的方法 'AR模式']</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a>
                                    <div>    public function getMinLimit () {        $sql = "...";        $result = yii::app()->db->createCo</div>
                                </li>
                                <li><a href="/article/2846.htm"
                                       title="solr StatsComponent(聚合统计)" target="_blank">solr StatsComponent(聚合统计)</a>
                                    <span class="text-muted">eksliang</span>
<a class="tag" taget="_blank" href="/search/solr%E8%81%9A%E5%90%88%E6%9F%A5%E8%AF%A2/1.htm">solr聚合查询</a><a class="tag" taget="_blank" href="/search/solr+stats/1.htm">solr stats</a>
                                    <div>StatsComponent 
转载请出自出处:http://eksliang.iteye.com/blog/2169134 
http://eksliang.iteye.com/ 一、概述 
       Solr可以利用StatsComponent 实现数据库的聚合统计查询,也就是min、max、avg、count、sum的功能 
  二、参数</div>
                                </li>
                                <li><a href="/article/2973.htm"
                                       title="百度一道面试题" target="_blank">百度一道面试题</a>
                                    <span class="text-muted">greemranqq</span>
<a class="tag" taget="_blank" href="/search/%E4%BD%8D%E8%BF%90%E7%AE%97/1.htm">位运算</a><a class="tag" taget="_blank" href="/search/%E7%99%BE%E5%BA%A6%E9%9D%A2%E8%AF%95/1.htm">百度面试</a><a class="tag" taget="_blank" href="/search/%E5%AF%BB%E6%89%BE%E5%A5%87%E6%95%B0%E7%AE%97%E6%B3%95/1.htm">寻找奇数算法</a><a class="tag" taget="_blank" href="/search/bitmap+%E7%AE%97%E6%B3%95/1.htm">bitmap 算法</a>
                                    <div>那天看朋友提了一个百度面试的题目:怎么找出{1,1,2,3,3,4,4,4,5,5,5,5}  找出出现次数为奇数的数字. 
  
我这里复制的是原话,当然顺序是不一定的,很多拿到题目第一反应就是用map,当然可以解决,但是效率不高。 
  
还有人觉得应该用算法xxx,我是没想到用啥算法好...! 
  
还有觉得应该先排序... 
  
还有觉</div>
                                </li>
                                <li><a href="/article/3100.htm"
                                       title="Spring之在开发中使用SpringJDBC" target="_blank">Spring之在开发中使用SpringJDBC</a>
                                    <span class="text-muted">ihuning</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                                    <div>  
在实际开发中使用SpringJDBC有两种方式: 
  
1. 在Dao中添加属性JdbcTemplate并用Spring注入; 
    JdbcTemplate类被设计成为线程安全的,所以可以在IOC 容器中声明它的单个实例,并将这个实例注入到所有的 DAO 实例中。JdbcTemplate也利用了Java 1.5 的特定(自动装箱,泛型,可变长度</div>
                                </li>
                                <li><a href="/article/3227.htm"
                                       title="JSON API 1.0 核心开发者自述 | 你所不知道的那些技术细节" target="_blank">JSON API 1.0 核心开发者自述 | 你所不知道的那些技术细节</a>
                                    <span class="text-muted">justjavac</span>
<a class="tag" taget="_blank" href="/search/json/1.htm">json</a>
                                    <div>2013年5月,Yehuda Katz 完成了JSON API(英文,中文) 技术规范的初稿。事情就发生在 RailsConf 之后,在那次会议上他和 Steve Klabnik 就 JSON 雏形的技术细节相聊甚欢。在沟通单一 Rails 服务器库—— ActiveModel::Serializers 和单一 JavaScript 客户端库——&</div>
                                </li>
                                <li><a href="/article/3354.htm"
                                       title="网站项目建设流程概述" target="_blank">网站项目建设流程概述</a>
                                    <span class="text-muted">macroli</span>
<a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C/1.htm">工作</a>
                                    <div>一.概念 
网站项目管理就是根据特定的规范、在预算范围内、按时完成的网站开发任务。 
二.需求分析 
项目立项 
  我们接到客户的业务咨询,经过双方不断的接洽和了解,并通过基本的可行性讨论够,初步达成制作协议,这时就需要将项目立项。较好的做法是成立一个专门的项目小组,小组成员包括:项目经理,网页设计,程序员,测试员,编辑/文档等必须人员。项目实行项目经理制。 
客户的需求说明书 
  第一步是需</div>
                                </li>
                                <li><a href="/article/3481.htm"
                                       title="AngularJs 三目运算 表达式判断" target="_blank">AngularJs 三目运算 表达式判断</a>
                                    <span class="text-muted">qiaolevip</span>
<a class="tag" taget="_blank" href="/search/%E6%AF%8F%E5%A4%A9%E8%BF%9B%E6%AD%A5%E4%B8%80%E7%82%B9%E7%82%B9/1.htm">每天进步一点点</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E6%B0%B8%E6%97%A0%E6%AD%A2%E5%A2%83/1.htm">学习永无止境</a><a class="tag" taget="_blank" href="/search/%E4%BC%97%E8%A7%82%E5%8D%83%E8%B1%A1/1.htm">众观千象</a><a class="tag" taget="_blank" href="/search/AngularJS/1.htm">AngularJS</a>
                                    <div>事件回顾:由于需要修改同一个模板,里面包含2个不同的内容,第一个里面使用的时间差和第二个里面名称不一样,其他过滤器,内容都大同小异。希望杜绝If这样比较傻的来判断if-show or not,继续追究其源码。 
var b = "{{",
      a = "}}";
        this.startSymbol = function(a) {
</div>
                                </li>
                                <li><a href="/article/3608.htm"
                                       title="Spark算子:统计RDD分区中的元素及数量" target="_blank">Spark算子:统计RDD分区中的元素及数量</a>
                                    <span class="text-muted">superlxw1234</span>
<a class="tag" taget="_blank" href="/search/spark/1.htm">spark</a><a class="tag" taget="_blank" href="/search/spark%E7%AE%97%E5%AD%90/1.htm">spark算子</a><a class="tag" taget="_blank" href="/search/Spark+RDD%E5%88%86%E5%8C%BA%E5%85%83%E7%B4%A0/1.htm">Spark RDD分区元素</a>
                                    <div>关键字:Spark算子、Spark RDD分区、Spark RDD分区元素数量 
  
  
Spark RDD是被分区的,在生成RDD时候,一般可以指定分区的数量,如果不指定分区数量,当RDD从集合创建时候,则默认为该程序所分配到的资源的CPU核数,如果是从HDFS文件创建,默认为文件的Block数。 
  
可以利用RDD的mapPartitionsWithInd</div>
                                </li>
                                <li><a href="/article/3735.htm"
                                       title="Spring 3.2.x将于2016年12月31日停止支持" target="_blank">Spring 3.2.x将于2016年12月31日停止支持</a>
                                    <span class="text-muted">wiselyman</span>
<a class="tag" taget="_blank" href="/search/Spring+3/1.htm">Spring 3</a>
                                    <div>      
        Spring 团队公布在2016年12月31日停止对Spring Framework 3.2.x(包含tomcat 6.x)的支持。在此之前spring团队将持续发布3.2.x的维护版本。 
  
       请大家及时准备及时升级到Spring </div>
                                </li>
                                <li><a href="/article/3862.htm"
                                       title="fis纯前端解决方案fis-pure" target="_blank">fis纯前端解决方案fis-pure</a>
                                    <span class="text-muted">zccst</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div>作者:zccst 
 
FIS通过插件扩展可以完美的支持模块化的前端开发方案,我们通过FIS的二次封装能力,封装了一个功能完备的纯前端模块化方案pure。 
 
 
1,fis-pure的安装 
$ fis install -g fis-pure 
$ pure -v 
0.1.4 
 
 
2,下载demo到本地 
git clone https://github.com/hefangshi/f</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>