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/1950232190038110208.htm"
                           title="day15|前端框架学习和算法" target="_blank">day15|前端框架学习和算法</a>
                        <span class="text-muted">universe_01</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a>
                        <div>T22括号生成先把所有情况都画出来,然后(在满足什么情况下)把不符合条件的删除。T78子集要画树状图,把思路清晰。可以用暴力法、回溯法和DFS做这个题DFS深度搜索:每个边都走完,再回溯应用:二叉树搜索,图搜索回溯算法=DFS+剪枝T200岛屿数量(非常经典BFS宽度把树状转化成队列形式,lambda匿名函数“一次性的小函数,没有名字”setup语法糖:让代码更简洁好写的语法ref创建:基本类型的</div>
                    </li>
                    <li><a href="/article/1950225785054883840.htm"
                           title="Java | 多线程经典问题 - 售票" target="_blank">Java | 多线程经典问题 - 售票</a>
                        <span class="text-muted">Ada54</span>

                        <div>一、售票需求1)同一个票池2)多个窗口卖票,不能出售同一张票二、售票问题代码实现(线程与进程小总结,请戳:Java|线程和进程,创建线程)step1:定义SaleWindow类实现Runnable接口,覆盖run方法step2:实例化SaleWindow对象,创建Thread对象,将SaleWindow作为参数传给Thread类的构造函数,然后通过Thread.start()方法启动线程step3</div>
                    </li>
                    <li><a href="/article/1950222345163567104.htm"
                           title="深入理解汇编语言子程序设计与系统调用" target="_blank">深入理解汇编语言子程序设计与系统调用</a>
                        <span class="text-muted">网安spinage</span>
<a class="tag" taget="_blank" href="/search/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/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><a class="tag" taget="_blank" href="/search/%E6%B1%87%E7%BC%96/1.htm">汇编</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>本文将全面解析汇编语言中子程序设计的核心技术以及系统调用的实现方法,涵盖参数传递的多种方式、堆栈管理、API调用等关键知识点,并提供实际案例演示。一、子程序设计:参数传递的艺术1.寄存器传参:高效简洁.386.modelflat,stdcalloptioncasemap:none.dataxdd5;定义变量ydd6sumdd?.code;函数定义:addxy1addxy1procpushebpmo</div>
                    </li>
                    <li><a href="/article/1950217809610993664.htm"
                           title="分支和循环(下)" target="_blank">分支和循环(下)</a>
                        <span class="text-muted">tryxr</span>
<a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a>
                        <div>写⼀个猜数字游戏游戏要求:1.电脑⾃动⽣成1~100的随机数2.玩家猜数字,猜数字的过程中,根据猜测数据的⼤⼩给出⼤了或⼩了的反馈,直到猜对,游戏结束1.随机数生成要想完成猜数字游戏,⾸先得产⽣随机数,那怎么产⽣随机数呢?randC语⾔提供了⼀个函数叫rand,这函数是可以⽣成随机数的,函数原型如下所⽰:intrand(void);rand函数会返回⼀个伪随机数,这个随机数的范围是在0~RAND_</div>
                    </li>
                    <li><a href="/article/1950214784112717824.htm"
                           title="C++ :vector的模拟" target="_blank">C++ :vector的模拟</a>
                        <span class="text-muted">诚自然成</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>目录一、vector的迭代器二、vector的构造函数默认构造函数参数构造函数迭代器范围构造函数拷贝构造函数swap:交换vector重载赋值符析构函数reserve:扩容vectorresize:调整大小push_back:添加元素empty:判空pop_back:后删获取大小与容量:size(),capacity()重载operator[]:元素访问insert:插入元素erase:删除一个元</div>
                    </li>
                    <li><a href="/article/1950208107430866944.htm"
                           title="python笔记14介绍几个魔法方法" target="_blank">python笔记14介绍几个魔法方法</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/python/1.htm">python</a>
                        <div>python笔记14介绍几个魔法方法先声明一下各位大佬,这是我的笔记。如有错误,恳请指正。另外,感谢您的观看,谢谢啦!(1).__doc__输出对应的函数,类的说明文档print(print.__doc__)print(value,...,sep='',end='\n',file=sys.stdout,flush=False)Printsthevaluestoastream,ortosys.std</div>
                    </li>
                    <li><a href="/article/1950202936449626112.htm"
                           title="Qwen3 大模型实战:使用 vLLM 部署与函数调用(Function Call)全攻略" target="_blank">Qwen3 大模型实战:使用 vLLM 部署与函数调用(Function Call)全攻略</a>
                        <span class="text-muted">曦紫沐</span>
<a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%A8%A1%E5%9E%8B/1.htm">大模型</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E9%83%A8%E7%BD%B2/1.htm">大模型部署</a><a class="tag" taget="_blank" href="/search/Qwen3/1.htm">Qwen3</a><a class="tag" taget="_blank" href="/search/vLLM/1.htm">vLLM</a><a class="tag" taget="_blank" href="/search/%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8/1.htm">函数调用</a>
                        <div>文章摘要本文将带你从零开始,深入掌握如何使用Qwen3-8B大语言模型,结合vLLM进行高性能部署,并通过函数调用(FunctionCall)实现模型与外部工具的智能联动。我们将详细讲解部署命令、调用方式、代码示例及实际应用场景,帮助你快速构建基于Qwen3的智能应用。一、Qwen3简介与部署环境准备Qwen3是通义千问系列的最新一代大语言模型,具备强大的自然语言理解和生成能力,尤其在函数调用、工</div>
                    </li>
                    <li><a href="/article/1950202684451647488.htm"
                           title="[spring6: Mvc-网关]-源码解析" target="_blank">[spring6: Mvc-网关]-源码解析</a>
                        <span class="text-muted"></span>

                        <div>推荐阅读:[spring6:Mvc-函数式编程]-源码解析GatewayServerMvcAutoConfiguration@AutoConfiguration(after={HttpClientAutoConfiguration.class,RestTemplateAutoConfiguration.class,RestClientAutoConfiguration.class,FilterAu</div>
                    </li>
                    <li><a href="/article/1950192217708621824.htm"
                           title="lesson20:Python函数的标注" target="_blank">lesson20:Python函数的标注</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/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>目录引言:为什么函数标注是现代Python开发的必备技能一、函数标注的基础语法1.1参数与返回值标注1.2支持的标注类型1.3Python3.9+的重大改进:标准集合泛型二、高级标注技巧与最佳实践2.1复杂参数结构标注2.2函数类型与回调标注2.3变量注解与类型别名三、静态类型检查工具应用3.1mypy:最流行的类型检查器3.2Pyright与IDE集成3.3运行时类型验证四、函数标注的工程价值与</div>
                    </li>
                    <li><a href="/article/1950185789447008256.htm"
                           title="Python 程序设计讲义(26):字符串的用法——字符的编码" target="_blank">Python 程序设计讲义(26):字符串的用法——字符的编码</a>
                        <span class="text-muted">睿思达DBA_WGX</span>
<a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/%E8%AE%B2%E4%B9%89/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>
                        <div>Python程序设计讲义(26):字符串的用法——字符的编码目录Python程序设计讲义(26):字符串的用法——字符的编码一、字符的编码二、`ASCII`编码三、`Unicode`编码四、使用`ord()`函数查询一个字符对应的`Unicode`编码五、使用`chr()`函数查询一个`Unicode`编码对应的字符六、`Python`字符串的特征一、字符的编码计算机默认只能处理二进制数,而不能处</div>
                    </li>
                    <li><a href="/article/1950179614320029696.htm"
                           title="python学习笔记(汇总)" target="_blank">python学习笔记(汇总)</a>
                        <span class="text-muted">朕的剑还未配妥</span>
<a class="tag" taget="_blank" href="/search/python%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/1.htm">python学习笔记整理</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</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%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>文章目录一.基础知识二.python中的数据类型三.运算符四.程序的控制结构五.列表六.字典七.元组八.集合九.字符串十.函数十一.解决bug一.基础知识print函数字符串要加引号,数字可不加引号,如print(123.4)print('小谢')print("洛天依")还可输入表达式,如print(1+3)如果使用三引号,print打印的内容可不在同一行print("line1line2line</div>
                    </li>
                    <li><a href="/article/1950164355706318848.htm"
                           title="001 Configuration结构体构造" target="_blank">001 Configuration结构体构造</a>
                        <span class="text-muted">盖世灬英雄z</span>
<a class="tag" taget="_blank" href="/search/DramSys/1.htm">DramSys</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
                        <div>目录DramSys代码分析1Configuration结构体构造1.1`from_path`函数详解1.2构造过程总结这种设计的好处2Simulator例化过程2.1instantiateInitiatorDramSys代码分析1Configuration结构体构造好的,我们来详细解释一下DRAMSysConfiguration.cpp文件中from_path函数的配置构造过程。这个文件是DRAM</div>
                    </li>
                    <li><a href="/article/1950163694096805888.htm"
                           title="运算符重载" target="_blank">运算符重载</a>
                        <span class="text-muted">紫诺不离</span>

                        <div>+、-、*、/、++、--、==、!=、*->、&&、||...对于内置数据类型,编译器知道如何做运算,编译器不知道如何让两个类进行运算如果向让自定义数据类型进行+法运算,就需要重载+运算符在成员函数或者全局函数里,重写一个+法运算符的函数函数名operator+(){}运算符重载也可以提供多个版本加法运算符类名+operator+(){};例:成员函数classPerson{public:Per</div>
                    </li>
                    <li><a href="/article/1950161706533580800.htm"
                           title="SQL笔记纯干货" target="_blank">SQL笔记纯干货</a>
                        <span class="text-muted">AI入门修炼</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><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/sql/1.htm">sql</a>
                        <div>软件:DataGrip2023.2.3,phpstudy_pro,MySQL8.0.12目录1.DDL语句(数据定义语句)1.1数据库操作语言1.2数据表操作语言2.DML语句(数据操作语言)2.1增删改2.2题2.3备份表3.DQL语句(数据查询语言)3.1查询操作3.2题一3.3题二4.多表详解4.1一对多4.2多对多5.多表查询6.窗口函数7.拓展:upsert8.sql注入攻击演示9.拆表</div>
                    </li>
                    <li><a href="/article/1950158303287898112.htm"
                           title="零数学基础理解AI核心概念:梯度下降可视化实战" target="_blank">零数学基础理解AI核心概念:梯度下降可视化实战</a>
                        <span class="text-muted">九章云极AladdinEdu</span>
<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/gpu%E7%AE%97%E5%8A%9B/1.htm">gpu算力</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/pytorch/1.htm">pytorch</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/1.htm">语言模型</a><a class="tag" taget="_blank" href="/search/opencv/1.htm">opencv</a>
                        <div>点击“AladdinEdu,同学们用得起的【H卡】算力平台”,H卡级别算力,按量计费,灵活弹性,顶级配置,学生专属优惠。用Python动画演示损失函数优化过程,数学公式具象化读者收获:直观理解模型训练本质,破除"数学恐惧症"当盲人登山者摸索下山路径时,他本能地运用了梯度下降算法。本文将用动态可视化技术,让你像感受重力一样理解AI训练的核心原理——无需任何数学公式推导。一、梯度下降:AI世界的"万有</div>
                    </li>
                    <li><a href="/article/1950146206252462080.htm"
                           title="OnJava8-学习分享(附资源)" target="_blank">OnJava8-学习分享(附资源)</a>
                        <span class="text-muted">李超同学</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E4%B9%A6%E7%B1%8D/1.htm">书籍</a><a class="tag" taget="_blank" href="/search/onjava8/1.htm">onjava8</a>
                        <div>本书是布鲁斯•埃克尔时隔15年,继ThinkinginJava之后又一力作,基于Java的3个长期支持版(Java8、11、17),讲解Java核心语法,并对Java的核心变化进行详述。全书内容通俗易懂,配合示例讲解逐步深入,并结合实际开发需要,从语言底层设计出发,有效帮读者规避一些常见的开发陷阱。主体部分共22章,内容包含对象、操作符、控制流、初始化和清理、复用、多态、接口、内部类、集合、函数式</div>
                    </li>
                    <li><a href="/article/1950138760230400000.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><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/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>在C语言中,数组和指针是两个密切相关但又有显著区别的概念。下面我会详细解释它们之间的区别和联系。区别数组和指针在C语言中虽然经常一起使用,但它们是两个不同的概念,具有一些关键的区别:本质不同:数组:数组是一种数据结构,用于存储固定数量的同类型元素的连续内存块。数组名在某些上下文中(如取地址操作或sizeof操作符)代表整个数组,但在其他上下文中(如作为函数参数或用于指针算术)通常退化为指向数组第一</div>
                    </li>
                    <li><a href="/article/1950137599754563584.htm"
                           title="xgboost原理" target="_blank">xgboost原理</a>
                        <span class="text-muted">茶尽</span>

                        <div>阅读XGBoost与BoostedTree基学习器:CART每个叶子节点上面有一个分数不够厉害,所以找一个更强的模型treeensemble对每个样本的预测结果是每棵树预测分数的和目标函数采用boosting(additivetraining)方法,每一次都加入一个新的函数。依赖每个数据点上的误差函数的一阶导数和二阶导(区别于GBDT)。树的复杂度复杂度包含了一棵树里面的叶子个数和输出分数的L2模</div>
                    </li>
                    <li><a href="/article/1950125525758439424.htm"
                           title="window 显示驱动开发-Direct3D 呈现性能改进(四)" target="_blank">window 显示驱动开发-Direct3D 呈现性能改进(四)</a>
                        <span class="text-muted">程序员王马</span>
<a class="tag" taget="_blank" href="/search/windows%E5%9B%BE%E5%BD%A2%E6%98%BE%E7%A4%BA%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/1.htm">windows图形显示驱动开发</a><a class="tag" taget="_blank" href="/search/%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/1.htm">驱动开发</a>
                        <div>调用资源创建、映射和取消映射函数的行为更改对于WDDM1.3及更高版本驱动程序实现的这些函数,Direct3D运行时为映射默认方案提供一组受限的输入值。这些受限值仅适用于支持功能级别11.1及更高版本的驱动程序。CreateResource(D3D11)函数—这些输入D3D11DDIARG_CREATERESOURCE结构成员受到限制:调用资源创建、映射和取消映射函数的行为更改对于WDDM1.3及</div>
                    </li>
                    <li><a href="/article/1950122628761055232.htm"
                           title="使用Python操作Excel,删重复数据及keep参数用法并保存的例子" target="_blank">使用Python操作Excel,删重复数据及keep参数用法并保存的例子</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/excel/1.htm">excel</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/Python%E7%BC%96%E7%A8%8B/1.htm">Python编程</a><a class="tag" taget="_blank" href="/search/Python%E5%AD%A6%E4%B9%A0/1.htm">Python学习</a><a class="tag" taget="_blank" href="/search/%E6%8A%80%E8%83%BD%E5%88%86%E4%BA%AB/1.htm">技能分享</a>
                        <div>01Ex按列标题删重复的数据解析:我们使用了pandas库读取Excel文件,并使用drop_duplicates()函数删除重复数据。其中,subset参数指定了删除重复数据的列(列名),keep参数指定了保留哪个重复记录(默认为第一个记录)。inplace=True参数表示在原始数据上进行操作。最后,我们使用to_excel()函数将处理后的数据,保存到一个新的Excel文件中,其中index</div>
                    </li>
                    <li><a href="/article/1950122432392130560.htm"
                           title="R语言笔记Day1(排序、筛选以及分类汇总))" target="_blank">R语言笔记Day1(排序、筛选以及分类汇总))</a>
                        <span class="text-muted">养猪场小老板</span>

                        <div>一、排序1、单变量序列排序2、数据表(矩阵)排序二、筛选三、分类汇总一、排序1、单变量序列排序rank、sort和order函数>aa[1]315#rank用来计算序列中每个元素的秩#这里的“秩”可以理解为该元素在序列中由小到大排列的次序#上面例子给出的序列[3,1,5]中,1最小,5最大,3居中#于是1的秩为1,3的秩为2,5的秩为3,(3,1,5)对应的秩的结果就是(2,1,3)>rank(a</div>
                    </li>
                    <li><a href="/article/1950117835502055424.htm"
                           title="Python day27" target="_blank">Python day27</a>
                        <span class="text-muted">赵英英俊</span>
<a class="tag" taget="_blank" href="/search/Python%E8%AE%AD%E7%BB%83/1.htm">Python训练</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>@浙大疏锦行Pythonday27内容:Python中的装饰器:封装的思想,使用@注解将函数封装起来从而实现在函数运行前后完成目标操作,即将被封装函数的行为视为整体运行过程中的一部分无返回值:函数无返回值,只需要完成封装逻辑即可。有返回值:函数有返回值,函数有返回值时,需要在装饰器函数位置接受返回值并返回给用户。importtimedefdisplay_time(func):"""支持任意参数的时</div>
                    </li>
                    <li><a href="/article/1950114809991196672.htm"
                           title="10. addEventListener 参数有哪些" target="_blank">10. addEventListener 参数有哪些</a>
                        <span class="text-muted">yqcoder</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95-CSS/1.htm">前端面试-CSS</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>总结el.addEventListener(type,listener,useCapture)el:事件对象type:事件类型listener:事件处理函数useCapture:布尔或对象,布尔时false为冒泡,true为捕获。对象时,{captrue:false,once:false,passive:false}addEventListener是用于为元素绑定事件监听器的标准方法,支持更灵活的</div>
                    </li>
                    <li><a href="/article/1950110272614100992.htm"
                           title="OpenGL里相机的运动控制" target="_blank">OpenGL里相机的运动控制</a>
                        <span class="text-muted">qq_42987967</span>
<a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">计算机图形学学习笔记</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%A0%81%E7%9B%B8%E6%9C%BA/1.htm">数码相机</a>
                        <div>相机的核心构造一个是glm::lookAt函数,一个是glm::perspective函数,本文相机的一切运动都在于如何构建相应的参数传入上述两个函数里。glm::mat4glm::lookAt(glm::vec3const&eye,//相机所在位置glm::vec3const¢er,//要凝视的点glm::vec3const&up//相机上向量);glm::mat4perspective</div>
                    </li>
                    <li><a href="/article/1950082036358246400.htm"
                           title="ESP32学习-按键中断" target="_blank">ESP32学习-按键中断</a>
                        <span class="text-muted">风过^无痕</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a>
                        <div>前提知识:freertos消息队列1.使用流程1.GPIO配置2.创建消息队列3.创建消息队列数据输入线程任务4.使能中断5.添加中断处理函数2.代码示例#include#include#include"freertos/FreeRTOS.h"#include"freertos/task.h"#include"freertos/queue.h"#include"driver/gpio.h"stat</div>
                    </li>
                    <li><a href="/article/1950081592827375616.htm"
                           title="如何在Power Query中对不同行,列名排序一致进行转置?" target="_blank">如何在Power Query中对不同行,列名排序一致进行转置?</a>
                        <span class="text-muted">Data_Skill</span>

                        <div>之前的案例都是列数及行数相同,那如果是不同的情况下,该如何处理呢?原表:同行不同列原表目标表:同行不同列目标表此时我们可以通过另外一个分组函数来进行处理。Table.GroupTable.Group(tableastable,keyasany,aggregatedColumnsaslist,optionalgroupKindasnullablenumber,optionalcomparerasnu</div>
                    </li>
                    <li><a href="/article/1950078131993899008.htm"
                           title="C#返回两个数组或多个数组,函数方法的一种写法" target="_blank">C#返回两个数组或多个数组,函数方法的一种写法</a>
                        <span class="text-muted">zhannghong2003</span>
<a class="tag" taget="_blank" href="/search/C%23/1.htm">C#</a><a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a>
                        <div>public(float[]x,float[]y)GetPolygonCollider2DPoints(PolygonCollider2Dobjects){Vector2[]points=objects.points;float[]x=newfloat[points.Length];float[]y=newfloat[points.Length];for(inti=0;i<points.Lengt</div>
                    </li>
                    <li><a href="/article/1950074471545106432.htm"
                           title="C++-coroutines协程 将自定义类型转为awaitable(可等待)类型的两种方法" target="_blank">C++-coroutines协程 将自定义类型转为awaitable(可等待)类型的两种方法</a>
                        <span class="text-muted">mrbone11</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Coroutines/1.htm">Coroutines</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><a class="tag" taget="_blank" href="/search/%E5%8D%8F%E7%A8%8B/1.htm">协程</a><a class="tag" taget="_blank" href="/search/coroutines/1.htm">coroutines</a>
                        <div>文章目录前言重载operatorco_await定义promise_type::await_transform函数前言阅读本篇文章时,已经假定你有对协程的基本概念,如果没有,可以阅读我写的这篇协程入门文章我们已经知道,对于co_await\,expression的返回值必须是awaitable类型,即正确实现了如下三个函数:await_readyawait_suspendawait_resume对</div>
                    </li>
                    <li><a href="/article/1950068796244946944.htm"
                           title="Qt技术面试问题总结" target="_blank">Qt技术面试问题总结</a>
                        <span class="text-muted">查理_Erik</span>
<a class="tag" taget="_blank" href="/search/qt/1.htm">qt</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/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>1.你知道什么是双指针吗,有没有在项目上用过,什么情况下使用双指针?答:就是两级指针,一级指针指向对象的地址,两级指针指向一级指针的地址,一般用于指针的传参,链表操作,两维数组。2.假设我在一个函数外定义了一个空指针,把它作为形参传入到函数中,我在函数中使用这个指针new了一个内存,请问这段运行这段代码会出现什么后果?那我应该如何传入一个空指针形参进函数并正确new一个内存呢?答:使用二级指针,或</div>
                    </li>
                    <li><a href="/article/1950061867435094016.htm"
                           title="c++ STL容器 --- 列表initializer_list" target="_blank">c++ STL容器 --- 列表initializer_list</a>
                        <span class="text-muted">qiuqiuyaq</span>
<a class="tag" taget="_blank" href="/search/STL%E5%AE%B9%E5%99%A8/1.htm">STL容器</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a>
                        <div>包含头文件在标准库中的容器可以直接用等号的方式初始化容器→直接用等号赋值{}列表就是一个{}数据一般情况下,如果想采用{}的方式初始化,类当中必须要有与之相匹配的参数的构造函数提供了一个构造函数,用initializer_list当做构造函数的参数,就可以实现我们想要的效果(有几个参数都可以)initializer_list主要是用在构造函数当中,可以忽略参数的个数去做初始化(两个、三个、多个..</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>