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/1943116632628981760.htm"
                           title="Three.js 实现导出模型文件(.glb,.gltf)功能 GLTFExporter" target="_blank">Three.js 实现导出模型文件(.glb,.gltf)功能 GLTFExporter</a>
                        <span class="text-muted"></span>

                        <div>Three.js提供了导出(.glb,.gltf)文件的APIGLTFExporter用于实现场景内容导出模型文件的功能导出模型文件主要使用parse方法,该方法接收三个参数:1.scene:要导出的场景对象。2.onComplete:解析完成后的回调函数,接收一个参数result,表示解析后的glTF数据。3.options:可选参数,用于配置导出的选项。下面是options的一些常用参数选项:</div>
                    </li>
                    <li><a href="/article/1943108565510189056.htm"
                           title="C语言正则表达式使用详解" target="_blank">C语言正则表达式使用详解</a>
                        <span class="text-muted"></span>

                        <div>标准的C和C++都不支持正则表达式,但有正则表达式的函数库提供这功能.C语言处理正则表达式常用的函数有regcomp()、regexec()、regfree()和regerror()。使用正则表达式步骤:1)编译正则表达式regcomp()2)匹配正则表达式regexec()3)释放正则表达式regfree()4)获取regcomp或者regexec产生错误,获取包含错误信息的字符串函数声明如下:</div>
                    </li>
                    <li><a href="/article/1943104779798507520.htm"
                           title="Python面试题:Python中的异步编程:详细讲解asyncio库的使用" target="_blank">Python面试题:Python中的异步编程:详细讲解asyncio库的使用</a>
                        <span class="text-muted">超哥同学</span>
<a class="tag" taget="_blank" href="/search/Python%E7%B3%BB%E5%88%97/1.htm">Python系列</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a>
                        <div>Python的异步编程是实现高效并发处理的一种方法,它使得程序能够在等待I/O操作时继续执行其他任务。在Python中,asyncio库是实现异步编程的主要工具。asyncio提供了一种机制来编写可以在单线程内并发执行的代码,适用于I/O密集型任务。以下是对asyncio库的详细讲解,包括基本概念、用法、示例以及注意事项。1.基本概念1.1协程(Coroutines)协程是一个特殊的函数,它可以被</div>
                    </li>
                    <li><a href="/article/1943103897472135168.htm"
                           title="嵌入式学习-Day6" target="_blank">嵌入式学习-Day6</a>
                        <span class="text-muted">不想学习\??!</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a>
                        <div>c语言day6模拟获取co2,pm2.5的数值,并对co2的浓度,pm2.5的浓度做出划分,详情划分在代码注释首先写写出模拟获取数值的函数,但是由于要对浓度划分,所以先枚举出来等级划分typedefenum{Excellent,//默认0往下递增Good,Average,Poor}QualityLevel;接着写出模拟获取co2函数(在这里用到了static关键字,静态函数能够确保只在co2的c文</div>
                    </li>
                    <li><a href="/article/1943098223434461184.htm"
                           title="从单体脚本到模块化设计:Python工程师的架构思维跃迁" target="_blank">从单体脚本到模块化设计:Python工程师的架构思维跃迁</a>
                        <span class="text-muted"></span>

                        <div>引言:从“一团乱麻”到“乐高积木”你是否曾经打开一个Python脚本,里面密密麻麻挤着上千行代码?函数相互缠绕,全局变量随处可见,想改一个小功能却心惊胆战,生怕牵一发而动全身?这就是典型的“单体脚本”(MonolithicScript)困境。作为过来人,我深知这种痛苦。本文将手把手带你跳出这个泥潭,掌握模块化设计的核心思想,并初步建立宝贵的架构设计思维,让你的代码从“勉强运行”跃迁到“优雅可维护”</div>
                    </li>
                    <li><a href="/article/1943097214737903616.htm"
                           title="python json 反序列化-V1" target="_blank">python json 反序列化-V1</a>
                        <span class="text-muted">CATTLECODE</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>在编程中,‌反序列化函数‌用于将序列化后的数据(如JSON、XML等格式)重新转换为程序可操作的对象或数据结构。以下是不同语言和场景下的实现方式及特点:‌1.Python中的反序列化‌‌(1)标准库json模块‌‌json.loads()‌:将JSON字符串反序列化为Python对象(如字典、列表)。importjsonjson_str='{"name":"Alice","age":25}'dat</div>
                    </li>
                    <li><a href="/article/1943086123010027520.htm"
                           title="大模型之Spring AI实战系列(三十二):Spring Boot + DeepSeek 实战指南:工具函数(Function Call)实战应用" target="_blank">大模型之Spring AI实战系列(三十二):Spring Boot + DeepSeek 实战指南:工具函数(Function Call)实战应用</a>
                        <span class="text-muted"></span>

                        <div>系列篇章No.文章1大模型之SpringAI实战系列(一):基础认知篇-开启智能应用开发之旅2大模型之SpringAI实战系列(二):SpringBoot+OpenAI打造聊天应用全攻略3大模型之SpringAI实战系列(三):SpringBoot+OpenAI实现聊天应用上下文记忆功能4大模型之SpringAI实战系列(四):SpringBoot+OpenAI使用OpenAIEmbedding实</div>
                    </li>
                    <li><a href="/article/1943080455200894976.htm"
                           title="408考研逐题详解:2010年第23题——系统调用" target="_blank">408考研逐题详解:2010年第23题——系统调用</a>
                        <span class="text-muted"></span>

                        <div>2010年第23题下列选项中,操作系统提供给应用程序的接口是()A.系统调用\qquadB.中断\qquadC.库函数\qquadD.原语解析本题考查对操作系统接口机制的理解,特别是应用程序如何与操作系统内核交互以请求服务(如文件操作、进程管理等)。系统调用:是操作系统内核为应用程序提供的一组预定义接口,允许应用程序请求内核服务(如I/O操作、进程控制、内存分配等)。应用程序通过特定的指令(如in</div>
                    </li>
                    <li><a href="/article/1943074268652826624.htm"
                           title="Hive适用语法" target="_blank">Hive适用语法</a>
                        <span class="text-muted">`whyYa</span>
<a class="tag" taget="_blank" href="/search/hive/1.htm">hive</a><a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E4%BB%93%E5%BA%93/1.htm">数据仓库</a>
                        <div>一、日期处理函数总结1.trunc()–取日期中当月第一天trunc(‘2022-12-05’,‘MM’)--取当月第一天2022-12-01trunc(‘2022-12-05’,‘Q’)--季度中的第一天2022-10-01trunc(‘2022-12-03’,‘YEAR’)–取当年第一天20222.last_day()–取当月最后一天last_day(‘2022-12-03’)3.month(</div>
                    </li>
                    <li><a href="/article/1943065190069235712.htm"
                           title="STM32F103C8T6标准库对SD卡读取数据的详细步骤" target="_blank">STM32F103C8T6标准库对SD卡读取数据的详细步骤</a>
                        <span class="text-muted">GC_June07</span>
<a class="tag" taget="_blank" href="/search/stm32/1.htm">stm32</a><a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%A1%AC%E4%BB%B6/1.htm">嵌入式硬件</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a>
                        <div>一、准备工作模块:STM32F103C8T6核心板、ST-LINK下载器、CH340、SD卡、SD卡模块、SD卡读卡器二、代码编写(标准库)①主函数:main说明:用串口1打印信息charSD_FileName[]="hello.txt";uint8_twrite_buf[]="WBQ牛逼!!!\r\n";intmain(void){     uint8_tres;   uart_init(960</div>
                    </li>
                    <li><a href="/article/1943059770470625280.htm"
                           title="共享内存的创建和使用" target="_blank">共享内存的创建和使用</a>
                        <span class="text-muted">Ring__Rain</span>
<a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a>
                        <div>以下是对ShareMemoryPubManager::CreateShm函数的详细解读,结合代码逻辑和Windows共享内存机制分析:1.函数功能概述该函数用于创建并映射一个共享内存区域,将其封装到自定义结构体SwathShareMemory中,并存储到成员变量m_shmQueue中。核心步骤包括:构造共享内存名称:基于shm_prefix和shm_id生成唯一标识。创建文件映射对象:调用Crea</div>
                    </li>
                    <li><a href="/article/1943047932152442880.htm"
                           title="让 Python 代码飙升330倍:从入门到精通的四种性能优化实践" target="_blank">让 Python 代码飙升330倍:从入门到精通的四种性能优化实践</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>花下猫语:性能优化是每个程序员的必修课,但你是否想过,除了更换算法,还有哪些“大招”?这篇文章堪称典范,它将一个普通的函数,通过四套组合拳,硬生生把性能提升了330倍!作者不仅展示了“术”,更传授了“道”。让我们一起跟随作者的思路,体验一次酣畅淋漓的优化之旅。PS.本文选自最新一期Python潮流周刊,如果你对优质文章感兴趣,诚心推荐你订阅我们的专栏。作者:ItamarTurner-Traurin</div>
                    </li>
                    <li><a href="/article/1943047805077614592.htm"
                           title="Golang中的panic" target="_blank">Golang中的panic</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AFgo/1.htm">后端go</a>
                        <div>前言Golang中当程序发生致命异常时(比如数组下标越界,注意这里的异常并不是error),Golang程序会panic(运行时恐慌)。当程序发生panic时,程序会执行当前栈中的defer函数列表。然后打印引发panic的具体信息,最后进程退出,本篇文章我们一起探讨Golang中的panic以及如何利用defer和recover来恢复这种致命的异常分析造成panic堆栈信息go体验AI代码助手代</div>
                    </li>
                    <li><a href="/article/1943036452690522112.htm"
                           title="React源码2 React中的工厂函数:createRoot()" target="_blank">React源码2 React中的工厂函数:createRoot()</a>
                        <span class="text-muted">gzzeason</span>
<a class="tag" taget="_blank" href="/search/ReactV18.2%E6%BA%90%E7%A0%81/1.htm">ReactV18.2源码</a><a class="tag" taget="_blank" href="/search/react.js/1.htm">react.js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>#ReactV18.2源码前置基础知识:工厂函数工厂函数是一种设计模式,用于动态创建对象或函数实例。其核心思想是通过封装对象创建的细节,提供统一的接口,从而增强代码的灵活性和可维护性,有一些核心作用:解耦创建逻辑:将对象的实例化过程与使用分离,调用方无需关心具体实现细节。动态生成:根据输入参数返回不同类型的对象或函数。统一接口:通过单一入口点管理多种创建场景。工厂函数由构造函数进阶而来,都是用来创</div>
                    </li>
                    <li><a href="/article/1943035066229780480.htm"
                           title="深度学习核心知识简介和模型调参" target="_blank">深度学习核心知识简介和模型调参</a>
                        <span class="text-muted">研术工坊</span>
<a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%9F%A5%E8%AF%86%E5%92%8C%E6%8A%80%E5%B7%A7/1.htm">深度学习知识和技巧</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>深度学习模型调优就像调制一道复杂的菜肴,需要掌握多种"调料"的用法。本文将为您详解这些关键"调料",帮助您烹饪出高性能的模型。###核心参数及其影响####1️⃣Loss(损失函数)**基本介绍**:衡量模型预测与真实值差距的指标,是模型优化的指南针。**生活类比**:想象你在教小孩认识动物:-**完美情况**:小孩看到猫说"猫",看到狗说"狗"→Loss=0-**有错误**:小孩看到猫说"狗"→</div>
                    </li>
                    <li><a href="/article/1943031913128194048.htm"
                           title="C语言—-数据的输入输出,printf,putchar,puts,scanf,getchar函数的使用及区别" target="_blank">C语言—-数据的输入输出,printf,putchar,puts,scanf,getchar函数的使用及区别</a>
                        <span class="text-muted">老虎0627</span>
<a class="tag" taget="_blank" href="/search/C%E8%AF%AD%E8%A8%80/1.htm">C语言</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>数据的输入C程序中实现输入的函数很多,下面逐个来进行介绍用printf函数输出数据printf函数的一般格式printf(“格式控制”,输出列表);例如#includeintmain(){inta=1;printf("a=%d\n"</div>
                    </li>
                    <li><a href="/article/1943007831775047680.htm"
                           title="EMQX 入门教程⑪——通过 ExHook 使用 gRPC 服务接收 EMQX 回调事件(已连接/已断开/已订阅/已发布...)" target="_blank">EMQX 入门教程⑪——通过 ExHook 使用 gRPC 服务接收 EMQX 回调事件(已连接/已断开/已订阅/已发布...)</a>
                        <span class="text-muted">小康师兄</span>
<a class="tag" taget="_blank" href="/search/EMQX/1.htm">EMQX</a><a class="tag" taget="_blank" href="/search/%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/1.htm">入门教程</a><a class="tag" taget="_blank" href="/search/EMQX/1.htm">EMQX</a><a class="tag" taget="_blank" href="/search/gRPC/1.htm">gRPC</a><a class="tag" taget="_blank" href="/search/ExHook/1.htm">ExHook</a><a class="tag" taget="_blank" href="/search/%E9%92%A9%E5%AD%90/1.htm">钩子</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>文章目录一、前文二、钩子函数介绍三、EMQX4.x的hook实现方法四、EMQX5.x的hook实现方法五、下载emqx-extension-examples六、修改Demo代码七、编译Demo代码八、运行Demo程序九、ExHook设置和启用十、更多日志十一、文档参考一、前文EMQX入门教程——导读二、钩子函数介绍exhook钩子函数可以理解成可挂载函数的点(HookPoint)。因为MQTT运</div>
                    </li>
                    <li><a href="/article/1942991827292450816.htm"
                           title="Outcome 使用教程" target="_blank">Outcome 使用教程</a>
                        <span class="text-muted"></span>

                        <div>Outcome使用教程outcomeProvidesverylightweightoutcomeandresult(non-Boostedition)项目地址:https://gitcode.com/gh_mirrors/ou/outcome1.项目介绍Outcome是一个C++14库,用于报告和处理函数失败。它可以作为异常处理机制的替代或补充。在某些场景下,使用C++的异常处理可能不合适,例如异</div>
                    </li>
                    <li><a href="/article/1942974312231268352.htm"
                           title="蓝桥杯C++组算法知识点整理 · 考前突击(上)【小白适用】" target="_blank">蓝桥杯C++组算法知识点整理 · 考前突击(上)【小白适用】</a>
                        <span class="text-muted">南星六月雪</span>
<a class="tag" taget="_blank" href="/search/C%2B%2B%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">C++学习笔记</a><a class="tag" taget="_blank" href="/search/%E5%8D%97%E6%98%9F%E5%85%AD%E6%9C%88%E9%9B%AA%E7%9A%84%E6%89%8B%E6%9C%AD/1.htm">南星六月雪的手札</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E8%93%9D%E6%A1%A5%E6%9D%AF/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/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a>
                        <div>【背景说明】本文的作者是一名算法竞赛小白,在第一次参加蓝桥杯之前希望整理一下自己会了哪些算法,于是有了本文的诞生。分享在这里也希望与众多学子共勉。如果时间允许的话,这一系列会分为上中下三部分和大家见面,祝大家竞赛顺利!【文风说明】本文主要会用代码+注释的方式来解释内容。相信学过编程的人都会发现程序比长篇大论更易理解!目录一、语言基础1.1编程基础1.2竞赛常用库函数1.2.1sort函数1.2.2</div>
                    </li>
                    <li><a href="/article/1942967510336860160.htm"
                           title="解决DevEco Studio预览器实时预览崩溃与超时问题的实用指南" target="_blank">解决DevEco Studio预览器实时预览崩溃与超时问题的实用指南</a>
                        <span class="text-muted"></span>

                        <div>以下是一些解决DevEcoStudio预览器实时预览崩溃、超时问题的方法:检查代码错误语法和引用错误:仔细检查代码中是否存在拼写错误、缺少分号等语法问题,以及是否引用了不存在的变量、函数或组件。例如,如果在使用findComponentById方法时,传入的组件ID不正确,就会导致预览器加载失败。逻辑错误:检查循环、条件判断等逻辑是否正确,确保代码能够正常执行,避免因逻辑错误导致预览器无法正确渲染</div>
                    </li>
                    <li><a href="/article/1942966753881550848.htm"
                           title="【C语言入门】函数返回局部变量指针的底层原理与实践陷阱" target="_blank">【C语言入门】函数返回局部变量指针的底层原理与实践陷阱</a>
                        <span class="text-muted"></span>

                        <div>第一章内存管理基础:C语言的内存布局与生命周期1.1C程序的内存分区C程序运行时,内存通常分为五个区域(以典型的32位系统为例):栈(Stack):自动分配和释放,用于存储函数参数、局部变量等临时数据由编译器管理,遵循“后进先出”原则,空间大小有限(通常几MB)变量生命周期:从声明处开始,到函数/代码块结束时自动销毁堆(Heap):手动分配(malloc/calloc/realloc)和释放(fr</div>
                    </li>
                    <li><a href="/article/1942964232681222144.htm"
                           title="Kafka生产者的初始化" target="_blank">Kafka生产者的初始化</a>
                        <span class="text-muted">夏日彩虹</span>
<a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a><a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a>
                        <div>创作内容丰富的干货文章很费心力,感谢点过此文章的读者,点一个关注鼓励一下作者,激励他分享更多的精彩好文,谢谢大家!把用户配置的KafkaProducer参数,赋值给KafkaProducer构造函数中userProvidedConfigs变量。获取clientId。获取用户配置的分区器。获取用户配置的retry.backoff.ms,默认值100毫秒,该参数的意思是设置在重试发送消息之前等待的时间</div>
                    </li>
                    <li><a href="/article/1942963347959902208.htm"
                           title="基于多设计模式的同步&异步日志系统--代码设计(六)" target="_blank">基于多设计模式的同步&异步日志系统--代码设计(六)</a>
                        <span class="text-muted">久念祈</span>
<a class="tag" taget="_blank" href="/search/%E6%97%A5%E5%BF%97%E7%B3%BB%E7%BB%9F/1.htm">日志系统</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a>
                        <div>目录日志器管理模块(单例)设计思想成员属性提供的接口代码实现:全局的日志器建造者代码实现全局接口的设计获取日志器的全局接口使用宏函数代理日志器的输出接口日志器管理模块(单例)用户可能会创建多个日志器,然后从中选取一个输出日志,那我们就需要将这些日志器管理起来,因此我们需要设计一个日志器管理模块。设计思想以日志器的名称作为唯一关键字将创建的日志器保存起来,允许用户通过日志器名称获取对应日志器,如果日</div>
                    </li>
                    <li><a href="/article/1942953265373179904.htm"
                           title="pytest测试框架完全指南" target="_blank">pytest测试框架完全指南</a>
                        <span class="text-muted"></span>

                        <div>目录1.安装与基础配置安装方法版本检查配置文件2.编写测试函数基本结构断言机制3.测试执行与报告基本执行方式常用命令行选项测试报告4.测试组织与管理测试类模块化测试5.高级测试功能Fixtures详解参数化测试异常测试进阶6.测试控制与标记跳过测试标记测试7.插件生态系统常用插件8.最佳实践9.完整示例项目10.学习资源pytest是Python生态中最流行、功能最强大的测试框架之一,它提供了简洁</div>
                    </li>
                    <li><a href="/article/1942942681483243520.htm"
                           title="C语言实现DNS客户端 | 详解dns_create_question函数的设计与实现" target="_blank">C语言实现DNS客户端 | 详解dns_create_question函数的设计与实现</a>
                        <span class="text-muted">(Charon)</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/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a>
                        <div>在实现一个简易的DNS查询客户端时,构造DNS报文是最关键的一步。DNS报文大致由两个部分组成:Header(报文头)Question(问题)本文聚焦于dns_create_question函数,即如何将用户输入的域名(如"www.example.com")编码为符合DNS协议格式的查询字段,并构造相关的qtype与qclass信息。一、DNSQuestion结构体定义回顾structdns_qu</div>
                    </li>
                    <li><a href="/article/1942942049451962368.htm"
                           title="window显示驱动开发—BGRA 扫描输出支持" target="_blank">window显示驱动开发—BGRA 扫描输出支持</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>为DXGI_FORMAT_B8G8R8A8_UNORM和DXGI_FORMAT_B8G8R8A8_UNORM_SRGB格式启用扫描输出位。因此,用户模式显示驱动程序应能够执行以下操作:处理对这些格式的主图面的请求。为使用这些格式创建的资源处理对其SetDisplayMode函数的调用。处理对其PresentDXGI函数的调用,以通过位块传输(bitblt)和翻转操作呈现这些格式。处理对其BltDX</div>
                    </li>
                    <li><a href="/article/1942940662932500480.htm"
                           title="Python MoviePy详解:从入门到实战的视频编辑指南" target="_blank">Python MoviePy详解:从入门到实战的视频编辑指南</a>
                        <span class="text-muted">detayun</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><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/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>一、MoviePy核心特性与优势MoviePy是一个基于Python的开源视频编辑库,其核心设计理念是基于时间的函数式组合。与传统视频编辑软件不同,它将视频视为可动态计算的函数集合,每个视频剪辑(Clip)本质上是一个时间函数F(t),返回指定时间点的图像帧或音频样本。这种设计赋予了开发者极大的灵活性:动态内容生成通过定义make_frame函数,可实现完全程序化的视频生成。例如:defgener</div>
                    </li>
                    <li><a href="/article/1942940157527257088.htm"
                           title="算法训练营DAY5 第二章 链表part02 补" target="_blank">算法训练营DAY5 第二章 链表part02 补</a>
                        <span class="text-muted"></span>

                        <div>首先补充链表part01的双链表、递归法反转链表双链表单链表中的指针域只能指向节点的下一个节点。双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。双链表既可以向前查询也可以向后查询。关键点:注意哨兵指针的初始化,前后都指向自己;在查询函数中,使用中点下标简化查询中的cur指针移动次数,从哨兵指针开始向后移动cur指针时,需要注意for循环中“inext=sentinelNod</div>
                    </li>
                    <li><a href="/article/1942940031207403520.htm"
                           title="代码训练营DAY13 第六章 二叉树part01" target="_blank">代码训练营DAY13 第六章 二叉树part01</a>
                        <span class="text-muted">_Coin_-</span>
<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>理论基础二叉树种类存储方式遍历方式深度优先搜索&广度优先搜索深度:前序遍历、中序遍历、后序遍历(中间在前or中or后,左右顺序固定)广度:二叉树定义递归遍历(必须掌握)递归分析三步法1、确定递归函数的参数和返回值2、确定终止条件3、确定单层递归逻辑前序遍历144.二叉树的前序遍历-力扣(LeetCode)/***Definitionforabinarytreenode.*structTreeNod</div>
                    </li>
                    <li><a href="/article/1942938691391516672.htm"
                           title="在Golang中序列化JSON字符串的教程" target="_blank">在Golang中序列化JSON字符串的教程</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AFgo/1.htm">后端go</a>
                        <div>Marshal递归地遍历接口的值。如果遇到的值实现了Marshaler接口,并且不是一个nil指针,Marshal会调用它的MarshalJSON方法来产生JSON。Golang序列化JSON字符串要在Golang中序列化JSON字符串,请使用json.Marshal()函数。Golangjson.Marshal()函数返回接口的JSON编码。请看下面的代码。go体验AI代码助手代码解读复制代码/</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>