ES6学习笔记(十六)async函数

1.含义

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

async 函数是什么?一句话,它就是 Generator 函数的语法糖,号称异步的终极解决方案。

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

 1 const fs = require('fs');
 2 
 3 const readFile = function (fileName) {
 4   return new Promise(function (resolve, reject) {
 5     fs.readFile(fileName, function(error, data) {
 6       if (error) return reject(error);
 7       resolve(data);
 8     });
 9   });
10 };
11 
12 const gen = function* () {
13   const f1 = yield readFile('/etc/fstab');
14   const f2 = yield readFile('/etc/shells');
15   console.log(f1.toString());
16   console.log(f2.toString());
17 };

上面代码的函数gen可以写成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 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

(4)返回值是 Promise。

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

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

2.基本用法

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对象。

 1 function timeout(ms) {
 2   return new Promise((resolve) => {
 3     setTimeout(resolve, ms);
 4   });
 5 }
 6 
 7 async function asyncPrint(value, ms) {
 8   await timeout(ms);
 9   console.log(value);
10 }
11 
12 asyncPrint('hello world', 50);

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

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

 1 async function timeout(ms) {
 2   await new Promise((resolve) => {
 3     setTimeout(resolve, ms);
 4   });
 5 }
 6 
 7 async function asyncPrint(value, ms) {
 8   await timeout(ms);
 9   console.log(value);
10 }
11 
12 asyncPrint('hello world', 50);

async 函数有多种使用形式。

 1 // 函数声明
 2 async function foo() {}
 3 
 4 // 函数表达式
 5 const foo = async function () {};
 6 
 7 // 对象的方法
 8 let obj = { async foo() {} };
 9 obj.foo().then(...)
10 
11 // Class 的方法
12 class Storage {
13   constructor() {
14     this.cachePromise = caches.open('avatars');
15   }
16 
17   async getAvatar(name) {
18     const cache = await this.cachePromise;
19     return cache.match(`/avatars/${name}.jpg`);
20   }
21 }
22 
23 const storage = new Storage();
24 storage.getAvatar('jake').then(…);
25 
26 // 箭头函数
27 const foo = async () => {};

3.语法

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)[1<span style="color:#000000;">];
}
getTitle(</span>'https://tc39.github.io/ecma262/'<span style="color:#000000;">).then(console.log)
</span><span style="color:#008000;">//</span><span style="color:#008000;"> "ECMAScript 2017 Language Specification"</span></pre> 
   </div> 
   <p>上面代码中,函数<code>getTitle</code>内部有三个操作:抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行<code>then</code>方法里面的<code>console.log</code>。</p> 
   <h2 id="await-命令" class="await-命令">await 命令</h2> 
   <p>正常情况下,<span style="color:#ff0000;"><code>await</code></span>命令后面是一个 <span style="color:#ff0000;">Promise</span> 对象,返回该对象的结果。<span style="color:#ff0000;">如果不是 Promise 对象,就直接返回对应的值</span>。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> f() {
  </span><span style="color:#008000;">//</span><span style="color:#008000;"> 等同于</span>
  <span style="color:#008000;">//</span><span style="color:#008000;"> return 123;</span>
  <span style="color:#0000ff;">return</span> await 123<span style="color:#000000;">;
}

f().then(v </span>=><span style="color:#000000;"> console.log(v))
</span><span style="color:#008000;">//</span><span style="color:#008000;"> 123</span></pre> 
   </div> 
   <p>上面代码中,<code>await</code>命令的参数是数值<code>123</code>,这时等同于<code>return 123</code>。</p> 
   <p>另一种情况是,<span style="color:#ff0000;"><code>await</code></span>命令后面是一个<span style="color:#ff0000;"><code>thenable</code></span>对象(即定义<span style="color:#ff0000;"><code>then</code></span>方法的对象),那么<code>await</code>会将其等同于 Promise 对象。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> <span style="color:#000000;">class Sleep {
</span><span style="color:#008080;"> 2</span> <span style="color:#000000;">  constructor(timeout) {
</span><span style="color:#008080;"> 3</span>     <span style="color:#0000ff;">this</span>.timeout =<span style="color:#000000;"> timeout;
</span><span style="color:#008080;"> 4</span> <span style="color:#000000;">  }
</span><span style="color:#008080;"> 5</span> <span style="color:#000000;"><span style="color:#ff0000;">  then</span>(resolve, reject) {
</span><span style="color:#008080;"> 6</span>     const startTime =<span style="color:#000000;"> Date.now();
</span><span style="color:#008080;"> 7</span> <span style="color:#000000;">    setTimeout(
</span><span style="color:#008080;"> 8</span>       () => resolve(Date.now() -<span style="color:#000000;"> startTime),
</span><span style="color:#008080;"> 9</span>       <span style="color:#0000ff;">this</span><span style="color:#000000;">.timeout
</span><span style="color:#008080;">10</span> <span style="color:#000000;">    );
</span><span style="color:#008080;">11</span> <span style="color:#000000;">  }
</span><span style="color:#008080;">12</span> <span style="color:#000000;">}
</span><span style="color:#008080;">13</span> 
<span style="color:#008080;">14</span> (async () =><span style="color:#000000;"> {
</span><span style="color:#008080;">15</span>   const actualTime = await <span style="color:#0000ff;">new</span> Sleep(1000<span style="color:#000000;">);
</span><span style="color:#008080;">16</span> <span style="color:#000000;">  console.log(actualTime);
</span><span style="color:#008080;">17</span> })();</pre> 
   </div> 
   <p>上面代码中,<code>await</code>命令后面是一个<code>Sleep</code>对象的实例。这个实例不是 Promise 对象,但是因为定义了<code>then</code>方法,<code>await</code>会将其视为<code>Promise</code>处理。</p> 
   <p><code>await</code>命令后面的 Promise 对象如果变为<code>reject</code>状态,则<code>reject</code>的参数会被<code>catch</code>方法的回调函数接收到。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> f() {
  await Promise.reject(</span>'出错了'<span style="color:#000000;">);
}

f()
.then(v </span>=><span style="color:#000000;"> console.log(v))
.</span><span style="color:#0000ff;">catch</span>(e =><span style="color:#000000;"> console.log(e))
</span><span style="color:#008000;">//</span><span style="color:#008000;"> 出错了</span></pre> 
   </div> 
   <p>注意,上面代码中,<code>await</code>语句前面没有<code>return</code>,但是<code>reject</code>方法的参数依然传入了<code>catch</code>方法的回调函数。这里如果在<code>await</code>前面加上<code>return</code>,效果是一样的。</p> 
   <p>任何一个<code>await</code>语句后面的 Promise 对象变为<code>reject</code>状态,那么整个<code>async</code>函数都会中断执行。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> f() {
  await Promise.reject(</span>'出错了'<span style="color:#000000;">);
  await Promise.resolve(</span>'hello world'); <span style="color:#008000;">//</span><span style="color:#008000;"> 不会执行</span>
}</pre> 
   </div> 
   <p>上面代码中,第二个<code>await</code>语句是不会执行的,因为第一个<code>await</code>语句状态变成了<code>reject</code>。</p> 
   <p>有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个<code>await</code>放在<code>try...catch</code>结构里面,这样不管这个异步操作是否成功,第二个<code>await</code>都会执行。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> f() {
  </span><span style="color:#0000ff;">try</span><span style="color:#000000;"> {
    await Promise.reject(</span>'出错了'<span style="color:#000000;">);
  } </span><span style="color:#0000ff;">catch</span><span style="color:#000000;">(e) {
  }
  </span><span style="color:#0000ff;">return</span> await Promise.resolve('hello world'<span style="color:#000000;">);
}

f()
.then(v </span>=><span style="color:#000000;"> console.log(v))
</span><span style="color:#008000;">//</span><span style="color:#008000;"> hello world</span></pre> 
   </div> 
   <p>另一种方法是<code>await</code>后面的 Promise 对象再跟一个<code>catch</code>方法,处理前面可能出现的错误。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> f() {
  await Promise.reject(</span>'出错了'<span style="color:#000000;">)
    .</span><span style="color:#0000ff;">catch</span>(e =><span style="color:#000000;"> console.log(e));
  </span><span style="color:#0000ff;">return</span> await Promise.resolve('hello world'<span style="color:#000000;">);
}

f()
.then(v </span>=><span style="color:#000000;"> console.log(v))
</span><span style="color:#008000;">//</span><span style="color:#008000;"> 出错了</span><span style="color:#008000;">
//</span><span style="color:#008000;"> hello world</span></pre> 
   </div> 
   <h2 id="错误处理" class="错误处理">错误处理</h2> 
   <p>如果<code>await</code>后面的异步操作出错,那么等同于<code>async</code>函数返回的 Promise 对象被<code>reject</code>。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> f() {
  await </span><span style="color:#0000ff;">new</span> Promise(<span style="color:#0000ff;">function</span><span style="color:#000000;"> (resolve, reject) {
    </span><span style="color:#0000ff;">throw</span> <span style="color:#0000ff;">new</span> Error('出错了'<span style="color:#000000;">);
  });
}

f()
.then(v </span>=><span style="color:#000000;"> console.log(v))
.</span><span style="color:#0000ff;">catch</span>(e =><span style="color:#000000;"> console.log(e))
</span><span style="color:#008000;">//</span><span style="color:#008000;"> Error:出错了</span></pre> 
   </div> 
   <p>上面代码中,<code>async</code>函数<code>f</code>执行后,<code>await</code>后面的 Promise 对象会抛出一个错误对象,导致<code>catch</code>方法的回调函数被调用,它的参数就是抛出的错误对象。具体的执行机制,可以参考后文的“async 函数的实现原理”。</p> 
   <p>防止出错的方法,也是将其放在<code>try...catch</code>代码块之中。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> f() {
  </span><span style="color:#0000ff;">try</span><span style="color:#000000;"> {
    await </span><span style="color:#0000ff;">new</span> Promise(<span style="color:#0000ff;">function</span><span style="color:#000000;"> (resolve, reject) {
      </span><span style="color:#0000ff;">throw</span> <span style="color:#0000ff;">new</span> Error('出错了'<span style="color:#000000;">);
    });
  } </span><span style="color:#0000ff;">catch</span><span style="color:#000000;">(e) {
  }
  </span><span style="color:#0000ff;">return</span> await('hello world'<span style="color:#000000;">);
}</span></pre> 
   </div> 
   <p>如果有多个<code>await</code>命令,可以统一放在<span style="color:#ff0000;"><code>try...catch</code></span>结构中。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> async <span style="color:#0000ff;">function</span><span style="color:#000000;"> main() {
</span><span style="color:#008080;"> 2</span>   <span style="color:#0000ff;">try</span><span style="color:#000000;"> {
</span><span style="color:#008080;"> 3</span>     const val1 =<span style="color:#000000;"> await firstStep();
</span><span style="color:#008080;"> 4</span>     const val2 =<span style="color:#000000;"> await secondStep(val1);
</span><span style="color:#008080;"> 5</span>     const val3 =<span style="color:#000000;"> await thirdStep(val1, val2);
</span><span style="color:#008080;"> 6</span> 
<span style="color:#008080;"> 7</span>     console.log('Final: '<span style="color:#000000;">, val3);
</span><span style="color:#008080;"> 8</span> <span style="color:#000000;">  }
</span><span style="color:#008080;"> 9</span>   <span style="color:#0000ff;">catch</span><span style="color:#000000;"> (err) {
</span><span style="color:#008080;">10</span> <span style="color:#000000;">    console.error(err);
</span><span style="color:#008080;">11</span> <span style="color:#000000;">  }
</span><span style="color:#008080;">12</span> }</pre> 
   </div> 
   <p>下面的例子使用<code>try...catch</code>结构,实现多次重复尝试。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> const superagent = require('superagent'<span style="color:#000000;">);
</span><span style="color:#008080;"> 2</span> const NUM_RETRIES = 3<span style="color:#000000;">;
</span><span style="color:#008080;"> 3</span> 
<span style="color:#008080;"> 4</span> async <span style="color:#0000ff;">function</span><span style="color:#000000;"> test() {
</span><span style="color:#008080;"> 5</span> <span style="color:#000000;">  let i;
</span><span style="color:#008080;"> 6</span>   <span style="color:#0000ff;">for</span> (i = 0; i < NUM_RETRIES; ++<span style="color:#000000;">i) {
</span><span style="color:#008080;"> 7</span>     <span style="color:#0000ff;">try</span><span style="color:#000000;"> {
</span><span style="color:#008080;"> 8</span>       await superagent.get('http://google.com/this-throws-an-error'<span style="color:#000000;">);
</span><span style="color:#008080;"> 9</span>       <span style="color:#0000ff;">break</span><span style="color:#000000;">;
</span><span style="color:#008080;">10</span>     } <span style="color:#0000ff;">catch</span><span style="color:#000000;">(err) {}
</span><span style="color:#008080;">11</span> <span style="color:#000000;">  }
</span><span style="color:#008080;">12</span>   console.log(i); <span style="color:#008000;">//</span><span style="color:#008000;"> 3</span>
<span style="color:#008080;">13</span> <span style="color:#000000;">}
</span><span style="color:#008080;">14</span> 
<span style="color:#008080;">15</span> test();</pre> 
   </div> 
   <p>上面代码中,如果<code>await</code>操作成功,就会使用<code>break</code>语句退出循环;如果失败,会被<code>catch</code>语句捕捉,然后进入下一轮循环。这个操作很神奇啊。</p> 
   <h2 id="使用注意点" class="使用注意点">使用注意点 </h2> 
   <p><span style="color:#ff0000;">第一点</span>,前面已经说过,<span style="color:#ff0000;"><code>await</code></span>命令后面的<span style="color:#ff0000;"><code>Promise</code></span>对象,运行结果可能是<span style="color:#ff0000;"><code>rejected</code></span>,所以最好把<span style="color:#ff0000;"><code>await</code></span>命令放在<span style="color:#ff0000;"><code>try...catch</code></span>代码块中。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> async <span style="color:#0000ff;">function</span><span style="color:#000000;"> myFunction() {
</span><span style="color:#008080;"> 2</span>   <span style="color:#0000ff;">try</span><span style="color:#000000;"> {
</span><span style="color:#008080;"> 3</span> <span style="color:#000000;">    await somethingThatReturnsAPromise();
</span><span style="color:#008080;"> 4</span>   } <span style="color:#0000ff;">catch</span><span style="color:#000000;"> (err) {
</span><span style="color:#008080;"> 5</span> <span style="color:#000000;">    console.log(err);
</span><span style="color:#008080;"> 6</span> <span style="color:#000000;">  }
</span><span style="color:#008080;"> 7</span> <span style="color:#000000;">}
</span><span style="color:#008080;"> 8</span> 
<span style="color:#008080;"> 9</span> <span style="color:#008000;">//</span><span style="color:#008000;"> 另一种写法</span>
<span style="color:#008080;">10</span> 
<span style="color:#008080;">11</span> async <span style="color:#0000ff;">function</span><span style="color:#000000;"> myFunction() {
</span><span style="color:#008080;">12</span> <span style="color:#000000;">  await somethingThatReturnsAPromise()
</span><span style="color:#008080;">13</span>   .<span style="color:#0000ff;">catch</span>(<span style="color:#0000ff;">function</span><span style="color:#000000;"> (err) {
</span><span style="color:#008080;">14</span> <span style="color:#000000;">    console.log(err);
</span><span style="color:#008080;">15</span> <span style="color:#000000;">  });
</span><span style="color:#008080;">16</span> }</pre> 
   </div> 
   <p><span style="color:#ff0000;">第二点,多个<code>await</code>命令后面的异步操作,如果不存在继发关系,最好让它们同时触发</span>。</p> 
   <div class="cnblogs_code"> 
    <pre>let foo =<span style="color:#000000;"> await getFoo();
let bar </span>= await getBar();</pre> 
   </div> 
   <p>上面代码中,<code>getFoo</code>和<code>getBar</code>是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有<code>getFoo</code>完成以后,才会执行<code>getBar</code>,完全可以让它们同时触发。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008000;">//</span><span style="color:#008000;"> 写法一</span>
let [foo, bar] =<span style="color:#000000;"> await Promise.all([getFoo(), getBar()]);

</span><span style="color:#008000;">//</span><span style="color:#008000;"> 写法二</span>
let fooPromise =<span style="color:#000000;"> getFoo();
let barPromise </span>=<span style="color:#000000;"> getBar();
let foo </span>=<span style="color:#000000;"> await fooPromise;
let bar </span>= await barPromise;</pre> 
   </div> 
   <p>上面两种写法,<code>getFoo</code>和<code>getBar</code>都是同时触发,这样就会缩短程序的执行时间。</p> 
   <p><span style="color:#ff0000;">第三点</span>,<span style="color:#ff0000;"><code>await</code></span>命令只能用在<span style="color:#ff0000;"><code>async</code></span>函数之中,如果用在普通函数,就会报错。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> dbFuc(db) {
  let docs </span>=<span style="color:#000000;"> [{}, {}, {}];

  </span><span style="color:#008000;">//</span><span style="color:#008000;"> 报错</span>
  docs.forEach(<span style="color:#0000ff;">function</span><span style="color:#000000;"> (doc) {
    await db.post(doc);
  });
}</span></pre> 
   </div> 
   <p>上面代码会报错,因为<code>await</code>用在普通函数之中了。但是,如果将<code>forEach</code>方法的参数改成<code>async</code>函数,也有问题。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#0000ff;">function</span> dbFuc(db) { <span style="color:#008000;">//</span><span style="color:#008000;">这里不需要 async</span>
  let docs =<span style="color:#000000;"> [{}, {}, {}];

  </span><span style="color:#008000;">//</span><span style="color:#008000;"> 可能得到错误结果</span>
  docs.forEach(async <span style="color:#0000ff;">function</span><span style="color:#000000;"> (doc) {
    await db.post(doc);
  });
}</span></pre> 
   </div> 
   <p>上面代码可能不会正常工作,原因是这时三个<code>db.post</code>操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用<code>for</code>循环。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> dbFuc(db) {
  let docs </span>=<span style="color:#000000;"> [{}, {}, {}];

  </span><span style="color:#0000ff;">for</span><span style="color:#000000;"> (let doc of docs) {
    await db.post(doc);
  }
}</span></pre> 
   </div> 
   <p>如果确实希望多个请求并发执行,可以使用<span style="color:#ff0000;"><code>Promise.all</code></span>方法。当三个请求都会<code>resolved</code>时,下面两种写法效果相同。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> async <span style="color:#0000ff;">function</span><span style="color:#000000;"> dbFuc(db) {
</span><span style="color:#008080;"> 2</span>   let docs =<span style="color:#000000;"> [{}, {}, {}];
</span><span style="color:#008080;"> 3</span>   let promises = docs.map((doc) =><span style="color:#000000;"> db.post(doc));
</span><span style="color:#008080;"> 4</span> 
<span style="color:#008080;"> 5</span>   let results =<span style="color:#000000;"> await Promise.all(promises);
</span><span style="color:#008080;"> 6</span> <span style="color:#000000;">  console.log(results);
</span><span style="color:#008080;"> 7</span> <span style="color:#000000;">}
</span><span style="color:#008080;"> 8</span> 
<span style="color:#008080;"> 9</span> <span style="color:#008000;">//</span><span style="color:#008000;"> 或者使用下面的写法</span>
<span style="color:#008080;">10</span> 
<span style="color:#008080;">11</span> async <span style="color:#0000ff;">function</span><span style="color:#000000;"> dbFuc(db) {
</span><span style="color:#008080;">12</span>   let docs =<span style="color:#000000;"> [{}, {}, {}];
</span><span style="color:#008080;">13</span>   let promises = docs.map((doc) =><span style="color:#000000;"> db.post(doc));
</span><span style="color:#008080;">14</span> 
<span style="color:#008080;">15</span>   let results =<span style="color:#000000;"> [];
</span><span style="color:#008080;">16</span>   <span style="color:#0000ff;">for</span><span style="color:#000000;"> (let promise of promises) {
</span><span style="color:#008080;">17</span> <span style="color:#000000;">    results.push(await promise);
</span><span style="color:#008080;">18</span> <span style="color:#000000;">  }
</span><span style="color:#008080;">19</span> <span style="color:#000000;">  console.log(results);
</span><span style="color:#008080;">20</span> }</pre> 
   </div> 
   <p>目前,<code>esm</code>模块加载器支持顶层<code>await</code>,即<code>await</code>命令可以不放在 async 函数里面,直接使用。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008000;">//</span><span style="color:#008000;"> async 函数的写法</span>
const start = async () =><span style="color:#000000;"> {
  const res </span>= await fetch('google.com'<span style="color:#000000;">);
  </span><span style="color:#0000ff;">return</span><span style="color:#000000;"> res.text();
};

start().then(console.log);

</span><span style="color:#008000;">//</span><span style="color:#008000;"> 顶层 await 的写法</span>
const res = await fetch('google.com'<span style="color:#000000;">);
console.log(await res.text());</span></pre> 
   </div> 
   <p>上面代码中,第二种写法的脚本必须使用<code>esm</code>加载器,才会生效。</p> 
   <p><span style="color:#ff0000;">第四点</span>,async 函数可以保留运行堆栈。</p> 
   <div class="cnblogs_code"> 
    <pre>const a = () =><span style="color:#000000;"> {
  b().then(() </span>=><span style="color:#000000;"> c());
};</span></pre> 
   </div> 
   <p>上面代码中,函数<code>a</code>内部运行了一个异步任务<code>b()</code>。当<code>b()</code>运行的时候,函数<code>a()</code>不会中断,而是继续执行。等到<code>b()</code>运行结束,可能<code>a()</code>早就运行结束了,<code>b()</code>所在的上下文环境已经消失了。如果<code>b()</code>或<code>c()</code>报错,错误堆栈将不包括<code>a()</code>。</p> 
   <p>现在将这个例子改成<code>async</code>函数。</p> 
   <div class="cnblogs_code"> 
    <pre>const a = async () =><span style="color:#000000;"> {
  await b();
  c();
};</span></pre> 
   </div> 
   <p>上面代码中,<code>b()</code>运行的时候,<code>a()</code>是暂停执行,上下文环境都保存着。一旦<code>b()</code>或<code>c()</code>报错,错误堆栈将包括<code>a()</code>。</p> 
   <h1 id="async-函数的实现原理" class="async-函数的实现原理">4.async 函数的实现原理</h1> 
   <p><span style="color:#ff0000;">async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里</span>。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> fn(args) {
  </span><span style="color:#008000;">//</span><span style="color:#008000;"> ...</span>
<span style="color:#000000;">}

</span><span style="color:#008000;">//</span><span style="color:#008000;"> 等同于</span>

<span style="color:#0000ff;">function</span><span style="color:#000000;"> fn(args) {
  </span><span style="color:#0000ff;">return</span> spawn(<span style="color:#0000ff;">function</span>*<span style="color:#000000;"> () {
    </span><span style="color:#008000;">//</span><span style="color:#008000;"> ...</span>
<span style="color:#000000;">  });
}</span></pre> 
   </div> 
   <p>所有的<code>async</code>函数都可以写成上面的第二种形式,其中的<code>spawn</code>函数就是自动执行器。</p> 
   <p>下面给出<code>spawn</code>函数的实现,基本就是前文自动执行器的翻版。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> <span style="color:#0000ff;">function</span><span style="color:#000000;"> spawn(genF) {
</span><span style="color:#008080;"> 2</span>   <span style="color:#0000ff;">return</span> <span style="color:#0000ff;">new</span> Promise(<span style="color:#0000ff;">function</span><span style="color:#000000;">(resolve, reject) {
</span><span style="color:#008080;"> 3</span>     const gen =<span style="color:#000000;"> genF();
</span><span style="color:#008080;"> 4</span>     <span style="color:#0000ff;">function</span><span style="color:#000000;"> step(nextF) {
</span><span style="color:#008080;"> 5</span> <span style="color:#000000;">      let next;
</span><span style="color:#008080;"> 6</span>       <span style="color:#0000ff;">try</span><span style="color:#000000;"> {
</span><span style="color:#008080;"> 7</span>         next =<span style="color:#000000;"> nextF();
</span><span style="color:#008080;"> 8</span>       } <span style="color:#0000ff;">catch</span><span style="color:#000000;">(e) {
</span><span style="color:#008080;"> 9</span>         <span style="color:#0000ff;">return</span><span style="color:#000000;"> reject(e);
</span><span style="color:#008080;">10</span> <span style="color:#000000;">      }
</span><span style="color:#008080;">11</span>       <span style="color:#0000ff;">if</span><span style="color:#000000;">(next.done) {
</span><span style="color:#008080;">12</span>         <span style="color:#0000ff;">return</span><span style="color:#000000;"> resolve(next.value);
</span><span style="color:#008080;">13</span> <span style="color:#000000;">      }
</span><span style="color:#008080;">14</span>       Promise.resolve(next.value).then(<span style="color:#0000ff;">function</span><span style="color:#000000;">(v) {
</span><span style="color:#008080;">15</span>         step(<span style="color:#0000ff;">function</span>() { <span style="color:#0000ff;">return</span><span style="color:#000000;"> gen.next(v); });
</span><span style="color:#008080;">16</span>       }, <span style="color:#0000ff;">function</span><span style="color:#000000;">(e) {
</span><span style="color:#008080;">17</span>         step(<span style="color:#0000ff;">function</span>() { <span style="color:#0000ff;">return</span> gen.<span style="color:#0000ff;">throw</span><span style="color:#000000;">(e); });
</span><span style="color:#008080;">18</span> <span style="color:#000000;">      });
</span><span style="color:#008080;">19</span> <span style="color:#000000;">    }
</span><span style="color:#008080;">20</span>     step(<span style="color:#0000ff;">function</span>() { <span style="color:#0000ff;">return</span><span style="color:#000000;"> gen.next(undefined); });
</span><span style="color:#008080;">21</span> <span style="color:#000000;">  });
</span><span style="color:#008080;">22</span> }</pre> 
   </div> 
   <h1 id="与其他异步处理方法的比较" class="与其他异步处理方法的比较">5.与其他异步处理方法的比较</h1> 
   <p>我们通过一个例子,来看 <span style="color:#ff0000;">async</span> 函数与 <span style="color:#ff0000;">Promise</span>、<span style="color:#ff0000;">Generator</span> 函数的<span style="color:#ff0000;">比较</span>。</p> 
   <p>假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。</p> 
   <p>首先是 Promise 的写法。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> <span style="color:#0000ff;">function</span><span style="color:#000000;"> chainAnimationsPromise(elem, animations) {
</span><span style="color:#008080;"> 2</span> 
<span style="color:#008080;"> 3</span>   <span style="color:#008000;">//</span><span style="color:#008000;"> 变量ret用来保存上一个动画的返回值</span>
<span style="color:#008080;"> 4</span>   let ret = <span style="color:#0000ff;">null</span><span style="color:#000000;">;
</span><span style="color:#008080;"> 5</span> 
<span style="color:#008080;"> 6</span>   <span style="color:#008000;">//</span><span style="color:#008000;"> 新建一个空的Promise</span>
<span style="color:#008080;"> 7</span>   let p =<span style="color:#000000;"> Promise.resolve();
</span><span style="color:#008080;"> 8</span> 
<span style="color:#008080;"> 9</span>   <span style="color:#008000;">//</span><span style="color:#008000;"> 使用then方法,添加所有动画</span>
<span style="color:#008080;">10</span>   <span style="color:#0000ff;">for</span><span style="color:#000000;">(let anim of animations) {
</span><span style="color:#008080;">11</span>     p = p.then(<span style="color:#0000ff;">function</span><span style="color:#000000;">(val) {
</span><span style="color:#008080;">12</span>       ret =<span style="color:#000000;"> val;
</span><span style="color:#008080;">13</span>       <span style="color:#0000ff;">return</span><span style="color:#000000;"> anim(elem);
</span><span style="color:#008080;">14</span> <span style="color:#000000;">    });
</span><span style="color:#008080;">15</span> <span style="color:#000000;">  }
</span><span style="color:#008080;">16</span> 
<span style="color:#008080;">17</span>   <span style="color:#008000;">//</span><span style="color:#008000;"> 返回一个部署了错误捕捉机制的Promise</span>
<span style="color:#008080;">18</span>   <span style="color:#0000ff;">return</span> p.<span style="color:#0000ff;">catch</span>(<span style="color:#0000ff;">function</span><span style="color:#000000;">(e) {
</span><span style="color:#008080;">19</span>     <span style="color:#008000;">/*</span><span style="color:#008000;"> 忽略错误,继续执行 </span><span style="color:#008000;">*/</span>
<span style="color:#008080;">20</span>   }).then(<span style="color:#0000ff;">function</span><span style="color:#000000;">() {
</span><span style="color:#008080;">21</span>     <span style="color:#0000ff;">return</span><span style="color:#000000;"> ret;
</span><span style="color:#008080;">22</span> <span style="color:#000000;">  });
</span><span style="color:#008080;">23</span> 
<span style="color:#008080;">24</span> }</pre> 
   </div> 
   <p>虽然 Promise 的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(<code>then</code>、<code>catch</code>等等),操作本身的语义反而不容易看出来。</p> 
   <p>接着是 Generator 函数的写法。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> <span style="color:#0000ff;">function</span><span style="color:#000000;"> chainAnimationsGenerator(elem, animations) {
</span><span style="color:#008080;"> 2</span> 
<span style="color:#008080;"> 3</span>   <span style="color:#0000ff;">return</span> spawn(<span style="color:#0000ff;">function</span>*<span style="color:#000000;">() {
</span><span style="color:#008080;"> 4</span>     let ret = <span style="color:#0000ff;">null</span><span style="color:#000000;">;
</span><span style="color:#008080;"> 5</span>     <span style="color:#0000ff;">try</span><span style="color:#000000;"> {
</span><span style="color:#008080;"> 6</span>       <span style="color:#0000ff;">for</span><span style="color:#000000;">(let anim of animations) {
</span><span style="color:#008080;"> 7</span>         ret =<span style="color:#000000;"> yield anim(elem);
</span><span style="color:#008080;"> 8</span> <span style="color:#000000;">      }
</span><span style="color:#008080;"> 9</span>     } <span style="color:#0000ff;">catch</span><span style="color:#000000;">(e) {
</span><span style="color:#008080;">10</span>       <span style="color:#008000;">/*</span><span style="color:#008000;"> 忽略错误,继续执行 </span><span style="color:#008000;">*/</span>
<span style="color:#008080;">11</span> <span style="color:#000000;">    }
</span><span style="color:#008080;">12</span>     <span style="color:#0000ff;">return</span><span style="color:#000000;"> ret;
</span><span style="color:#008080;">13</span> <span style="color:#000000;">  });
</span><span style="color:#008080;">14</span> 
<span style="color:#008080;">15</span> }</pre> 
   </div> 
   <p>上面代码使用 Generator 函数遍历了每个动画,语义比 Promise 写法更清晰,用户定义的操作全部都出现在<code>spawn</code>函数的内部。这个写法的问题在于,必须有一个任务运行器,自动执行 Generator 函数,上面代码的<code>spawn</code>函数就是自动执行器,它返回一个 Promise 对象,而且必须保证<code>yield</code>语句后面的表达式,必须返回一个 Promise。</p> 
   <p>最后是 async 函数的写法。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> async <span style="color:#0000ff;">function</span><span style="color:#000000;"> chainAnimationsAsync(elem, animations) {
</span><span style="color:#008080;"> 2</span>   let ret = <span style="color:#0000ff;">null</span><span style="color:#000000;">;
</span><span style="color:#008080;"> 3</span>   <span style="color:#0000ff;">try</span><span style="color:#000000;"> {
</span><span style="color:#008080;"> 4</span>     <span style="color:#0000ff;">for</span><span style="color:#000000;">(let anim of animations) {
</span><span style="color:#008080;"> 5</span>       ret =<span style="color:#000000;"> await anim(elem);
</span><span style="color:#008080;"> 6</span> <span style="color:#000000;">    }
</span><span style="color:#008080;"> 7</span>   } <span style="color:#0000ff;">catch</span><span style="color:#000000;">(e) {
</span><span style="color:#008080;"> 8</span>     <span style="color:#008000;">/*</span><span style="color:#008000;"> 忽略错误,继续执行 </span><span style="color:#008000;">*/</span>
<span style="color:#008080;"> 9</span> <span style="color:#000000;">  }
</span><span style="color:#008080;">10</span>   <span style="color:#0000ff;">return</span><span style="color:#000000;"> ret;
</span><span style="color:#008080;">11</span> }</pre> 
   </div> 
   <p>可以看到 Async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。它将 Generator 写法中的<span style="color:#ff0000;">自动执行器</span>,改在<span style="color:#ff0000;">语言层面</span>提供,不暴露给用户,因此代码量最少。如果使用 Generator 写法,自动执行器需要用户自己提供。</p> 
   <h1 id="实例:按顺序完成异步操作" class="实例:按顺序完成异步操作">6.实例:按顺序完成异步操作</h1> 
   <p>实际开发中,经常遇到一组异步操作,需要按照顺序完成。比如,依次远程读取一组 URL,然后按照读取的顺序输出结果。</p> 
   <p>Promise 的写法如下。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> <span style="color:#0000ff;">function</span><span style="color:#000000;"> logInOrder(urls) {
</span><span style="color:#008080;"> 2</span>   <span style="color:#008000;">//</span><span style="color:#008000;"> 远程读取所有URL</span>
<span style="color:#008080;"> 3</span>   const textPromises = urls.map(url =><span style="color:#000000;"> {
</span><span style="color:#008080;"> 4</span>     <span style="color:#0000ff;">return</span> fetch(url).then(response =><span style="color:#000000;"> response.text());
</span><span style="color:#008080;"> 5</span> <span style="color:#000000;">  });
</span><span style="color:#008080;"> 6</span> 
<span style="color:#008080;"> 7</span>   <span style="color:#008000;">//</span><span style="color:#008000;"> 按次序输出</span>
<span style="color:#008080;"> 8</span>   textPromises.reduce((chain, textPromise) =><span style="color:#000000;"> {
</span><span style="color:#008080;"> 9</span>     <span style="color:#0000ff;">return</span> chain.then(() =><span style="color:#000000;"> textPromise)
</span><span style="color:#008080;">10</span>       .then(text =><span style="color:#000000;"> console.log(text));
</span><span style="color:#008080;">11</span> <span style="color:#000000;">  }, Promise.resolve());
</span><span style="color:#008080;">12</span> }</pre> 
   </div> 
   <p>上面代码使用<code>fetch</code>方法,同时远程读取一组 URL。每个<code>fetch</code>操作都返回一个 Promise 对象,放入<code>textPromises</code>数组。然后,<code>reduce</code>方法依次处理每个 Promise 对象,然后使用<code>then</code>,将所有 Promise 对象连起来,因此就可以依次输出结果。</p> 
   <p>这种写法不太直观,可读性比较差。下面是 async 函数实现。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> logInOrder(urls) {
  </span><span style="color:#0000ff;">for</span><span style="color:#000000;"> (const url of urls) {
    const response </span>=<span style="color:#000000;"> await fetch(url);
    console.log(await response.text());
  }
}</span></pre> 
   </div> 
   <p>上面代码确实大大简化,问题是所有远程操作都是继发。只有前一个 URL 返回结果,才会去读取下一个 URL,这样做效率很差,非常浪费时间。我们需要的是<span style="color:#ff0000;">并发发出远程请求</span>。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> async <span style="color:#0000ff;">function</span><span style="color:#000000;"> logInOrder(urls) {
</span><span style="color:#008080;"> 2</span>   <span style="color:#008000;">//</span><span style="color:#008000;"> 并发读取远程URL</span>
<span style="color:#008080;"> 3</span>   const textPromises = urls.map(async url =><span style="color:#000000;"> {
</span><span style="color:#008080;"> 4</span>     const response =<span style="color:#000000;"> await fetch(url);
</span><span style="color:#008080;"> 5</span>     <span style="color:#0000ff;">return</span><span style="color:#000000;"> response.text();
</span><span style="color:#008080;"> 6</span> <span style="color:#000000;">  });
</span><span style="color:#008080;"> 7</span> 
<span style="color:#008080;"> 8</span>   <span style="color:#008000;">//</span><span style="color:#008000;"> 按次序输出</span>
<span style="color:#008080;"> 9</span>   <span style="color:#0000ff;">for</span><span style="color:#000000;"> (const textPromise of textPromises) {
</span><span style="color:#008080;">10</span> <span style="color:#000000;">    console.log(await textPromise);
</span><span style="color:#008080;">11</span> <span style="color:#000000;">  }
</span><span style="color:#008080;">12</span> }</pre> 
   </div> 
   <p>上面代码中,虽然<code>map</code>方法的参数是<code>async</code>函数,但它是并发执行的,因为<span style="color:#ff0000;">只有<code>async</code>函数内部是继发执行,外部不受影响</span>。后面的<code>for..of</code>循环内部使用了<code>await</code>,因此实现了按顺序输出。</p> 
   <h1 id="异步遍历器" class="异步遍历器">7.异步遍历器</h1> 
   <p>《遍历器》一章说过,Iterator 接口是一种数据遍历的协议,只要调用遍历器对象的<code>next</code>方法,就会得到一个对象,表示当前遍历指针所在的那个位置的信息。<code>next</code>方法返回的对象的结构是<code>{value, done}</code>,其中<code>value</code>表示当前的数据的值,<code>done</code>是一个布尔值,表示遍历是否结束。</p> 
   <p>这里隐含着一个规定,<code>next</code>方法必须是同步的,只要调用就必须立刻返回值。也就是说,一旦执行<code>next</code>方法,就必须同步地得到<code>value</code>和<code>done</code>这两个属性。如果遍历指针正好指向同步操作,当然没有问题,但对于异步操作,就不太合适了。目前的解决方法是,Generator 函数里面的异步操作,返回一个 Thunk 函数或者 Promise 对象,即<code>value</code>属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而<code>done</code>属性则还是同步产生的。</p> 
   <p>ES2018 引入了“异步遍历器”(Async Iterator),为异步操作提供原生的遍历器接口,即<code>value</code>和<code>done</code>这两个属性都是异步产生。</p> 
   <h2 id="异步遍历的接口" class="异步遍历的接口">异步遍历的接口</h2> 
   <p>异步遍历器的最大的语法特点,就是调用遍历器的<code>next</code>方法,返回的是一个 Promise 对象。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#000000;">asyncIterator
  .next()
  .then(
    ({ value, done }) </span>=> <span style="color:#008000;">/*</span><span style="color:#008000;"> ... </span><span style="color:#008000;">*/</span><span style="color:#000000;">
  );</span></pre> 
   </div> 
   <p>上面代码中,<code>asyncIterator</code>是一个异步遍历器,调用<code>next</code>方法以后,返回一个 Promise 对象。因此,可以使用<code>then</code>方法指定,这个 Promise 对象的状态变为<code>resolve</code>以后的回调函数。回调函数的参数,则是一个具有<code>value</code>和<code>done</code>两个属性的对象,这个跟同步遍历器是一样的。</p> 
   <p>我们知道,一个对象的同步遍历器的接口,部署在<code>Symbol.iterator</code>属性上面。同样地,对象的异步遍历器接口,部署在<code>Symbol.asyncIterator</code>属性上面。不管是什么样的对象,只要它的<code>Symbol.asyncIterator</code>属性有值,就表示应该对它进行异步遍历。</p> 
   <p>下面是一个异步遍历器的例子。</p> 
   <div class="cnblogs_code"> 
    <pre><span style="color:#008080;"> 1</span> const asyncIterable = createAsyncIterable(['a', 'b'<span style="color:#000000;">]);
</span><span style="color:#008080;"> 2</span> const asyncIterator =<span style="color:#000000;"> asyncIterable[Symbol.asyncIterator]();
</span><span style="color:#008080;"> 3</span> 
<span style="color:#008080;"> 4</span> <span style="color:#000000;">asyncIterator
</span><span style="color:#008080;"> 5</span> <span style="color:#000000;">.next()
</span><span style="color:#008080;"> 6</span> .then(iterResult1 =><span style="color:#000000;"> {
</span><span style="color:#008080;"> 7</span>   console.log(iterResult1); <span style="color:#008000;">//</span><span style="color:#008000;"> { value: 'a', done: false }</span>
<span style="color:#008080;"> 8</span>   <span style="color:#0000ff;">return</span><span style="color:#000000;"> asyncIterator.next();
</span><span style="color:#008080;"> 9</span> <span style="color:#000000;">})
</span><span style="color:#008080;">10</span> .then(iterResult2 =><span style="color:#000000;"> {
</span><span style="color:#008080;">11</span>   console.log(iterResult2); <span style="color:#008000;">//</span><span style="color:#008000;"> { value: 'b', done: false }</span>
<span style="color:#008080;">12</span>   <span style="color:#0000ff;">return</span><span style="color:#000000;"> asyncIterator.next();
</span><span style="color:#008080;">13</span> <span style="color:#000000;">})
</span><span style="color:#008080;">14</span> .then(iterResult3 =><span style="color:#000000;"> {
</span><span style="color:#008080;">15</span>   console.log(iterResult3); <span style="color:#008000;">//</span><span style="color:#008000;"> { value: undefined, done: true }</span>
<span style="color:#008080;">16</span> });</pre> 
   </div> 
   <p>了解一下,异步遍历器不再深究。</p> 
   <h2 id="for-await---of" class="for-await---of">for await...of</h2> 
   <p>前面介绍过,<code>for...of</code>循环用于遍历同步的 Iterator 接口。新引入的<code>for await...of</code>循环,则是用于遍历异步的 Iterator 接口。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span><span style="color:#000000;"> f() {
  </span><span style="color:#0000ff;">for</span> await (const x of createAsyncIterable(['a', 'b'<span style="color:#000000;">])) {
    console.log(x);
  }
}
</span><span style="color:#008000;">//</span><span style="color:#008000;"> a</span><span style="color:#008000;">
//</span><span style="color:#008000;"> b</span></pre> 
   </div> 
   <h2 id="异步-Generator-函数" class="异步-Generator-函数">异步 Generator 函数</h2> 
   <p> 就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。</p> 
   <h2 id="yield--语句" class="yield--语句">yield* 语句</h2> 
   <p><code>yield*</code>语句也可以跟一个异步遍历器。</p> 
   <div class="cnblogs_code"> 
    <pre>async <span style="color:#0000ff;">function</span>*<span style="color:#000000;"> gen1() {
  yield </span>'a'<span style="color:#000000;">;
  yield </span>'b'<span style="color:#000000;">;
  </span><span style="color:#0000ff;">return</span> 2<span style="color:#000000;">;
}

async </span><span style="color:#0000ff;">function</span>*<span style="color:#000000;"> gen2() {
  </span><span style="color:#008000;">//</span><span style="color:#008000;"> result 最终会等于 2</span>
  const result = yield*<span style="color:#000000;"> gen1();
}</span></pre> 
   </div> 
   <p>上面代码中,<code>gen2</code>函数里面的<code>result</code>变量,最后的值是<code>2</code>。</p> 
   <p>与同步 Generator 函数一样,<code>for await...of</code>循环会展开<code>yield*</code>。</p> 
   <p>没有最好的方法,只有最适合的方法。</p> 
  </div> 
  <p>转载于:https://www.cnblogs.com/jixiaohua/p/10674686.html</p> 
 </div> 
</div>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1292661603430375424"></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">你可能感兴趣的:(ES6学习笔记(十六)async函数)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1882520992832614400.htm"
                           title="Python进阶—高级语法" target="_blank">Python进阶—高级语法</a>
                        <span class="text-muted">Echo.py</span>
<a class="tag" taget="_blank" href="/search/Python%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95/1.htm">Python基础语法</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>目录文章目录目录1、在==和is之间选择2、元组的相对不可变性3、字典中的键映射多个值4、Linux5、python中字典的key要求6、编码7、进制之间的转换8、关系运算符(时间处理)9、时间处理模块❶常用时间处理方法❷转化为13位时间戳10、三元运算符11、成员运算符12、For循环机制13、变量的分类14、闭包(函数的嵌套)15、函数(方法)的执行流程16、匿名函数17、Django和Fla</div>
                    </li>
                    <li><a href="/article/1882518967881363456.htm"
                           title="Python timeit的使用" target="_blank">Python timeit的使用</a>
                        <span class="text-muted">egzosn</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>假设您要测量代码段的执行时间。你是做什么?直到现在,我就像大多数人一样会做以下事情:登录后复制#导入时间start_time=time.time()"""某些代码"""end_time=time.time()print(f“执行时间为:{end_time-start_time}”)1.2.3.4.5.现在说我们要比较两个不同函数的执行时间,然后:登录后复制#导入时间deffunction_1(*参</div>
                    </li>
                    <li><a href="/article/1882518946091954176.htm"
                           title="【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error" target="_blank">【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error</a>
                        <span class="text-muted">egzosn</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>在前端开发中,JavaScript异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如Promise、async/await等),开发者常常会遇到Uncaught(inpromise)error错误。这个错误是由于未正确处理Promise的拒绝(rejection)而导致的,常常出现在异步操作失败的情况下。如果不妥善处理,可能会导致应用的不稳定和用户体验的下降。本文将深入分析Uncaugh</div>
                    </li>
                    <li><a href="/article/1882518721902211072.htm"
                           title="【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error" target="_blank">【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error</a>
                        <span class="text-muted">egzosn</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>在前端开发中,JavaScript异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如Promise、async/await等),开发者常常会遇到Uncaught(inpromise)error错误。这个错误是由于未正确处理Promise的拒绝(rejection)而导致的,常常出现在异步操作失败的情况下。如果不妥善处理,可能会导致应用的不稳定和用户体验的下降。本文将深入分析Uncaugh</div>
                    </li>
                    <li><a href="/article/1882507889562677248.htm"
                           title="Python异步: 什么时候使用异步?" target="_blank">Python异步: 什么时候使用异步?</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AFpython/1.htm">后端python</a>
                        <div>从广义上讲,Asyncio是新的、流行的、讨论广泛的和令人兴奋的。然而,对于何时应该在项目中采用它存在很多困惑。我们什么时候应该在Python中使用asyncio?在Python中使用Asyncio的原因在Python项目中使用asyncio可能有3个原因:使用asyncio以便在您的程序中采用协程。使用asyncio以使用异步编程范例。使用asyncio以使用非阻塞I/O。1.1.使用协程我们可</div>
                    </li>
                    <li><a href="/article/1882507756427079680.htm"
                           title="async++库的使用示例" target="_blank">async++库的使用示例</a>
                        <span class="text-muted">哎呦,帅小伙哦</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/async%2B%2B/1.htm">async++</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/async%2B%2B/1.htm">async++</a>
                        <div>1、普通异步函数如前面的博客介绍的,这个库中提供了async::spawn方法,这个方法通常用来启动异步函数,这个框架会利用线程池去完成函数,因此要注意数据安全。正因为将任务放到了单独的线程执行,并且还有调度开销,因此简单的任务最好不要使用这种方法,得不偿失。示例代码如下:voiddemo_async1(){std::mutexmutex;autotask1=async::spawn([&mute</div>
                    </li>
                    <li><a href="/article/1882505865722916864.htm"
                           title="c语言--第一章练习题" target="_blank">c语言--第一章练习题</a>
                        <span class="text-muted">weixin_45958231</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/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>第一章练习题1.一个C程序的执行是从(A)。A)本程序的main函数开始,到main函数结束B)本程序文件的第一个函数开始,到本程序文件的最后一个函数结束C)本程序文件的第一个函数开始,到本程序main函数结束D)本程序的main函数开始,到本程序文件的最后一个函数结束A)本程序的main函数开始,到main函数结束正确。C程序的执行总是从main函数开始,并在main函数中的return语句(如</div>
                    </li>
                    <li><a href="/article/1882480650464849920.htm"
                           title="C#中的委托和事件" target="_blank">C#中的委托和事件</a>
                        <span class="text-muted">190043</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>委托委托(Delegate)是C#中一种特殊的类型,它用于封装方法的引用。委托可以看作是类型安全的函数指针,允许你将方法作为参数传递给其他方法、从方法返回方法或者存储在变量中以备后用。委托在事件处理、回调函数和异步编程等场景中非常有用。委托的基本概念定义委托:首先需要定义一个委托类型,指定它可以引用的方法签名(包括返回类型和参数列表)。实例化委托:然后创建该委托类型的实例,并将其与具体的方法关联起</div>
                    </li>
                    <li><a href="/article/1882479515754622976.htm"
                           title="C# 委托和事件委托" target="_blank">C# 委托和事件委托</a>
                        <span class="text-muted">火星papa</span>
<a class="tag" taget="_blank" href="/search/C%23/1.htm">C#</a><a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a><a class="tag" taget="_blank" href="/search/delegate/1.htm">delegate</a><a class="tag" taget="_blank" href="/search/event/1.htm">event</a>
                        <div>C#委托和事件委托目录C#委托和事件委托什么是委托?1、概念2、代码描述委托什么是事件委托?1、概念2、代码描述事件委托利用事件委托在不同窗体传参1、建窗体2、主窗体代码:3、子窗体代码:什么是委托?1、概念  委托(Delegate)类似于C或C++中函数的指针。是存有对某个方法的引用的一种引用类型变量。引用委托可在运行时被改变,特别是用于实现事件和回调方法。其来自于System.Delegat</div>
                    </li>
                    <li><a href="/article/1882478633113677824.htm"
                           title="Python 变量和简单数据类型(变量)" target="_blank">Python 变量和简单数据类型(变量)</a>
                        <span class="text-muted">钢铁男儿</span>
<a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A/1.htm">从入门到精通</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>变量每个变量都存储一个值——与变量相关联的信息。变量的命名规则①变量名只能包含字母、数字和下划线。变量名可以字母或下划线打头,但不能以数字打头,例如,可将变量名message_1,但不能将变量名1_message。②变量名不能包含空格,但可使用下划线来分割其中的单词。例如,变量名greeting_message可行,但变量名greetingmessage会引发错误。③不要将Python关键字和函数</div>
                    </li>
                    <li><a href="/article/1882475607166611456.htm"
                           title="c#委托和事件" target="_blank">c#委托和事件</a>
                        <span class="text-muted">真的没事鸭</span>
<a class="tag" taget="_blank" href="/search/C%23/1.htm">C#</a><a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a>
                        <div>目录委托1,什么是委托2,委托的定义3,使用委托4,委托的多播事件1,什么是事件2,事件的定义3,订阅和触发事件4,事件的访问控制5,事件的多播委托1,什么是委托委托是一个函数指针,可以存储方法的引用,并且能够在运行时调用这些方法。委托允许将方法作为参数传递,或将方法作为返回值返回2,委托的定义例://定义一个委托publicdelegatevoidMyDelegate(stringmessage</div>
                    </li>
                    <li><a href="/article/1882470942987317248.htm"
                           title="C语言学习记录——通讯录(静态内存)" target="_blank">C语言学习记录——通讯录(静态内存)</a>
                        <span class="text-muted">qiyi.sky</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/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a>
                        <div>分模块contact.h类型定义,函数声明。contact.c函数的实现test.c测试通讯录的模块test.c#include"contact.h"voidmenu(){printf("\n");printf("1.add2.del\n");printf("3.search4.modify\n");printf("5.sort6.print\n");printf("0.exit\n");prin</div>
                    </li>
                    <li><a href="/article/1882469301999431680.htm"
                           title="C#委托和事件的区别" target="_blank">C#委托和事件的区别</a>
                        <span class="text-muted">犯罪刚回来</span>
<a class="tag" taget="_blank" href="/search/Visual/1.htm">Visual</a><a class="tag" taget="_blank" href="/search/Studio/1.htm">Studio</a><a class="tag" taget="_blank" href="/search/unity3d/1.htm">unity3d</a><a class="tag" taget="_blank" href="/search/c%23/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>一、概念1、委托:将方法以变量的形式传递,并且以方法的形式执行。他是类,是引用类型。2、事件:功能被限制的一个委托变量。它的类型是委托类型。3、委托的三种形式3.1、delegate:四步(声明,实例化,注册方法,调用)3.2、Action:添加的方法不能有返回值3.3、Func:添加的方法要有返回值3.4、lamda表达式:方法只使用一次,没有多次使用的话使用二、案例1、委托、匿名函数、lamd</div>
                    </li>
                    <li><a href="/article/1882463373497528320.htm"
                           title="Python 入门路线(2025 极简无废话版)" target="_blank">Python 入门路线(2025 极简无废话版)</a>
                        <span class="text-muted">墨鱼爆蛋</span>
<a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><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%BC%96%E7%A8%8B/1.htm">编程</a>
                        <div>大家好,梳理一个Python从入门到精通路线大家都挺忙的,突出一个无废话注:时间仅供参考第一阶段:基础入门(0-3个月)1.Python基础语法开发环境搭建(Python安装、IDE选择)变量和数据类型运算符和表达式控制流(if/else、循环)函数定义与调用基本输入输出2.数据结构基础列表(List)和元组(Tuple)字典(Dict)和集合(Set)字符串处理文件操作3.错误处理try/exc</div>
                    </li>
                    <li><a href="/article/1882457956709101568.htm"
                           title="Effective C++ 规则41:了解隐式接口和编译期多态" target="_blank">Effective C++ 规则41:了解隐式接口和编译期多态</a>
                        <span class="text-muted">哎呦,帅小伙哦</span>
<a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/effective/1.htm">effective</a><a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a>
                        <div>1、隐式接口C++中的隐式接口是指类或者模板中不显式声明为接口的一部分,但仍然可以像接口一样使用的成员或方法。隐式接口通常指那些不显式声明为虚函数的函数或者方法,但在多态上下文中仍然能表现出类似接口的行为。隐式接口通常出现在模板编程中,尤其是模板类型推导、SFINAE(SubstitutionFailureIsNotAnError)技术或者类型特性(typetraits)等编译期机制中。它使得类和</div>
                    </li>
                    <li><a href="/article/1882457073204129792.htm"
                           title="Redisson 实现分布式锁" target="_blank">Redisson 实现分布式锁</a>
                        <span class="text-muted">程序员jin</span>
<a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/1.htm">编程开发</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a>
                        <div>文章目录一.锁介绍二.什么是分布式锁三.为什么需要分布式锁四.分布式锁实现五.分布式锁注意事项六.看门狗机制七.Redisson实现分布式锁一.锁介绍单机锁:用于一台服务器的同步执行。分布式锁:用于多台服务器之间的同步执行。有限资源的情况下,控制同一时间(段)只有某些线程(用户/服务器)能访问到资源。Java实现锁:synchronized关键字、并发包的类。Javasynchronized实现锁</div>
                    </li>
                    <li><a href="/article/1882456189946621952.htm"
                           title="在Vue页面中实现平滑滚动功能" target="_blank">在Vue页面中实现平滑滚动功能</a>
                        <span class="text-muted">种花的人_</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a>
                        <div>这是一个实现平滑滚动的函数,可以让页面在滚动到指定位置时产生缓动效果该函数依赖于Math.easeInOutQuad函数和requestAnimFrame函数,其中Math.easeInOutQuad函数用于计算当前滚动位置的值(根据时间、起始值、变化量和持续时间),requestAnimFrame函数用于实现动画效果。函数的参数包括:to:目标滚动位置;duration:滚动持续时间,默认为50</div>
                    </li>
                    <li><a href="/article/1882449883114303488.htm"
                           title="我的ROS学习笔记(四)" target="_blank">我的ROS学习笔记(四)</a>
                        <span class="text-muted">zenpluck</span>
<a class="tag" taget="_blank" href="/search/%E8%87%AA%E5%8A%A8%E9%A9%BE%E9%A9%B6/1.htm">自动驾驶</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a>
                        <div>提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档publisher程序代码学习前言一、包含消息类型声明二、创建发布者对象三、创建并填充消息对象四、发布消息五、消息发布循环1.节点是否停止工作的检查2.控制消息发布频率总结前言发布者程序包含了很多之前不懂的知识,刚开始也许只能复制粘贴代码来运行实例,但最终目的还是为了自己能够编写这些代码。因此,弄明白哪部分代码是什么意思非常有必要,不</div>
                    </li>
                    <li><a href="/article/1882444084925231104.htm"
                           title="类与对象中的六大默认成员函数万字详解" target="_blank">类与对象中的六大默认成员函数万字详解</a>
                        <span class="text-muted">kk\n</span>
<a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>目录1、类的6个默认成员函数2、构造函数2.1、概念2.2、特性3、析构函数3.1、概念3.2、特性4、拷贝构造函数4.1、概念4.2、特性5、赋值运算符重载5.1、运算符重载5.2、赋值运算符重载5.3、前置++和后置++重载1、类的6个默认成员函数如果一个类中什么成员都没有,简称为空类。空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。默认成员函数:用</div>
                    </li>
                    <li><a href="/article/1882443203114758144.htm"
                           title="Python Selenium使用cookie实现自动登录WB" target="_blank">Python Selenium使用cookie实现自动登录WB</a>
                        <span class="text-muted">haerxiluo</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/selenium/1.htm">selenium</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a>
                        <div>文章目录前言一、预登陆获取cookie1)cookie处理2)预登陆二、登录测试前言模拟登录WB是实现WB网页爬虫的第一步,现在的WB网页版有个sinavisitsystem,只有登录过后才能获取更多内容。本文使用selenium通过预登陆保存cookie到本地,之后重复登录只需要提取本地cookie即可免去每次扫码或者输密码登录。一、预登陆获取cookie1)cookie处理先简单引入两个函数实</div>
                    </li>
                    <li><a href="/article/1882443076803293184.htm"
                           title="学习笔记之——3DGS-SLAM系列代码解读" target="_blank">学习笔记之——3DGS-SLAM系列代码解读</a>
                        <span class="text-muted">gwpscut</span>
<a class="tag" taget="_blank" href="/search/3D/1.htm">3D</a><a class="tag" taget="_blank" href="/search/Gaussian/1.htm">Gaussian</a><a class="tag" taget="_blank" href="/search/Splatting/1.htm">Splatting</a><a class="tag" taget="_blank" href="/search/%283DGS%29/1.htm">(3DGS)</a><a class="tag" taget="_blank" href="/search/3DGS/1.htm">3DGS</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%B8%89%E7%BB%B4%E9%87%8D%E5%BB%BA/1.htm">三维重建</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/1.htm">计算机视觉</a><a class="tag" taget="_blank" href="/search/3d/1.htm">3d</a>
                        <div>最近对一系列基于3DGaussianSplatting(3DGS)SLAM的工作的源码进行了测试与解读。为此写下本博客mark一下所有的源码解读以及对应的代码配置与测试记录~其中工作1~5的原理解读见博客:学习笔记之——3DGaussianSplatting及其在SLAM与自动驾驶上的应用调研_3dgaussiansplattingslam-CSDN博客文章浏览阅读5.3k次,点赞53次,收藏92</div>
                    </li>
                    <li><a href="/article/1882431099104194560.htm"
                           title="《CANOpen》 学习笔记3" target="_blank">《CANOpen》 学习笔记3</a>
                        <span class="text-muted">wumingdezu</span>
<a class="tag" taget="_blank" href="/search/CANopen/1.htm">CANopen</a><a class="tag" taget="_blank" href="/search/CANopen/1.htm">CANopen</a><a class="tag" taget="_blank" href="/search/sdo/1.htm">sdo</a><a class="tag" taget="_blank" href="/search/%E9%80%9A%E4%BF%A1/1.htm">通信</a>
                        <div>《CANOpen》学习笔记3《CANOpen协议——SDO介绍》注:这里的SDO模式有点类似于TCP/IP中的TCP模式。即『服务器-客户端』模式本文主要以一个实例进行讲解。1.目的:实现节点2的数据传送到节点32.手段:使用SDO进行传送SDO不能实现从节点之间的数据直接传送3.分析:SDO通讯可以描述成客户/服务器模式,SDO的客户/服务器通讯模式如图所示。两个节点中请求进行读写操作的节点为客</div>
                    </li>
                    <li><a href="/article/1882428327080292352.htm"
                           title="CANopen学习笔记" target="_blank">CANopen学习笔记</a>
                        <span class="text-muted">卡钦斯基</span>
<a class="tag" taget="_blank" href="/search/%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/1.htm">通信协议</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a>
                        <div>1.CANopen的预定义报文ID分类CANopen在设计时,对其定义为小网络、控制信号的实时通讯:报文传输采用CAN标准帧格式。即11bit的ID域,以尽量减小传输时间。网络控制报均采用数据最小字节数。比如心跳报文,只有1个字节数据。实时更新的过程数据无需接收方报文应答。即采用生产消费模型,降低总线负载。需要接收方确认的配置参数一般都时采用快速单字传输。即1个报文最多传达1个32bit的参数变量</div>
                    </li>
                    <li><a href="/article/1882428200353591296.htm"
                           title="Java单例模式详解--七种单例模式实现+单例安全+实际应用场景" target="_blank">Java单例模式详解--七种单例模式实现+单例安全+实际应用场景</a>
                        <span class="text-muted">飞天葫芦侠</span>
<a class="tag" taget="_blank" href="/search/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">面向对象设计模式</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/1.htm">单例模式</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/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>单例模式保证了一个类只有一个实例,并且提供了一个全局访问点。单例模式的主要作用是节省公共资源,方便控制,避免多个实例造成的问题。实现单例模式的三点:私有构造函数私有静态变量维护对象实例公有静态方法提供获取实例对象七种单例模式实现1.静态类:第一次运行初始化,全局使用2.懒汉模式(线程不安全):懒汉模式是指在第一次获取实例时才创建对象,实现了延迟加载,构造函数返回当前对象实例,但多个访问者同时获取对</div>
                    </li>
                    <li><a href="/article/1882427314814382080.htm"
                           title="深入理解旋转位置编码(RoPE)及其在大型语言模型中的应用" target="_blank">深入理解旋转位置编码(RoPE)及其在大型语言模型中的应用</a>
                        <span class="text-muted">tangjunjun-owen</span>
<a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B-%E5%A4%9A%E6%A8%A1%E6%80%81%E5%A4%A7%E6%A8%A1%E5%9E%8B/1.htm">语言模型-多模态大模型</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/1.htm">语言模型</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/1.htm">自然语言处理</a><a class="tag" taget="_blank" href="/search/RoPE/1.htm">RoPE</a><a class="tag" taget="_blank" href="/search/%E6%97%8B%E8%BD%AC%E4%BD%8D%E7%BD%AE%E7%BC%96%E7%A0%81/1.htm">旋转位置编码</a>
                        <div>文章目录前言一、旋转位置编码原理1、RoPE概述2、复数域内的旋转1、位置编码生成2、应用位置编码二、RoPE的实现细节1、RotaryEmbedding类设计2、apply_rotary_pos_emb函数3、demo_apply_rotary_pos_emb函数三、完整RoPE代码Demo前言随着自然语言处理(NLP)领域的快速发展,预训练的语言模型如BERT、GPT系列、PaLM、Qwen等</div>
                    </li>
                    <li><a href="/article/1882423403881754624.htm"
                           title="Python笔记1.2(open、logging、os、shutil、glob、decode、encode、pickle、tqdm)" target="_blank">Python笔记1.2(open、logging、os、shutil、glob、decode、encode、pickle、tqdm)</a>
                        <span class="text-muted">qq742234984</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a>
                        <div>Python笔记1.1(datetime、argparse、sys、overwrite、eval、json、os、zfill、endswith、traceback、深浅拷贝)Python笔记2(函数参数、面向对象、装饰器、高级函数、捕获异常、dir)Python笔记1.214、withopen()asfile和open()参数详解15、logging日志的等级logging.basicConfig</div>
                    </li>
                    <li><a href="/article/1882422899168571392.htm"
                           title="ES6 (三)字符串的扩展、模板字符串、模板编译、标签模板" target="_blank">ES6 (三)字符串的扩展、模板字符串、模板编译、标签模板</a>
                        <span class="text-muted">ChrisP3616</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%881%E2%80%94%E2%80%94%E6%B1%87%E6%80%BB/1.htm">前端工程师1——汇总</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%883%E2%80%94%E2%80%94ES6/1.htm">前端工程师3——ES6</a><a class="tag" taget="_blank" href="/search/%E5%AD%97%E7%AC%A6%E4%B8%B2/1.htm">字符串</a><a class="tag" taget="_blank" href="/search/es6/1.htm">es6</a><a class="tag" taget="_blank" href="/search/unicode/1.htm">unicode</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a>
                        <div>ES6(三)字符串的扩展、模板字符串、模板编译、标签模板文章目录ES6(三)字符串的扩展、模板字符串、模板编译、标签模板1.字符的Unicode表示法2.字符串的遍历器接口3.直接输入U+2028和U+20294.JSON.stringify()的改造5.模板字符串6.实例:模板编译(==Review==)7.==标签模板==8.模板字符串的限制1.字符的Unicode表示法ES6加强了对Unic</div>
                    </li>
                    <li><a href="/article/1882410545487867904.htm"
                           title="JavaScript高级学习:ES6新特性07——数组在ES6中的使用" target="_blank">JavaScript高级学习:ES6新特性07——数组在ES6中的使用</a>
                        <span class="text-muted">北凉冬</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/es6/1.htm">es6</a>
                        <div>提示:本文为JavaScript栏目:JavaScript高级系列——ES6新特性章节第七章JavaScript高级学习:ES6新特性07——数组在ES6中的使用前言数组在ES6中的使用求数组的最大值ES5的方式求数组最大值ES6的方式使用数组扩展运算符求最大值ES6扩展运算符的使用扩展运算符传参扩展运算符操作伪数组复制数组传统复制数组ES6中使用扩展运算符复制数组ES5中复制数组合并数组ES6方</div>
                    </li>
                    <li><a href="/article/1882407015205105664.htm"
                           title="C语言二级 2025/1/20 周一" target="_blank">C语言二级 2025/1/20 周一</a>
                        <span class="text-muted">他在从中笑</span>
<a class="tag" taget="_blank" href="/search/C/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/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>20.关系表达式四、程序设计题1.请编写函数fun,其功能是:计算并输出3到n之间(含3和n)所有素数的平方根之和。例如,在主函数中从键盘给n输入100后,输出为:sum=148.874270。注意:要求n的值大于2但不大于100。部分源程序给出如下。请勿改动主函数main和其他函数中的任何内容,仅在函数fun的花括号中填入所编写的若干语句。#include#includedoublefun(in</div>
                    </li>
                    <li><a href="/article/1882402733533753344.htm"
                           title="【Rust自学】13.10. 性能对比:循环 vs. 迭代器" target="_blank">【Rust自学】13.10. 性能对比:循环 vs. 迭代器</a>
                        <span class="text-muted">SomeB1oody</span>
<a class="tag" taget="_blank" href="/search/Rust%E8%87%AA%E5%AD%A6/1.htm">Rust自学</a><a class="tag" taget="_blank" href="/search/rust/1.htm">rust</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>13.10.0.写在正文之前Rust语言在设计过程中收到了很多语言的启发,而函数式编程对Rust产生了非常显著的影响。函数式编程通常包括通过将函数作为值传递给参数、从其他函数返回它们、将它们分配给变量以供以后执行等等。在本章中,我们会讨论Rust的一些特性,这些特性与许多语言中通常称为函数式的特性相似:闭包迭代器使用闭包和迭代器改进I/O项目闭包和迭代器的性能(本文)喜欢的话别忘了点赞、收藏加关注</div>
                    </li>
                                <li><a href="/article/84.htm"
                                       title="继之前的线程循环加到窗口中运行" target="_blank">继之前的线程循环加到窗口中运行</a>
                                    <span class="text-muted">3213213333332132</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/thread/1.htm">thread</a><a class="tag" taget="_blank" href="/search/JFrame/1.htm">JFrame</a><a class="tag" taget="_blank" href="/search/JPanel/1.htm">JPanel</a>
                                    <div>之前写了有关java线程的循环执行和结束,因为想制作成exe文件,想把执行的效果加到窗口上,所以就结合了JFrame和JPanel写了这个程序,这里直接贴出代码,在窗口上运行的效果下面有附图。 
 

package thread;

import java.awt.Graphics;
import java.text.SimpleDateFormat;
import java.util</div>
                                </li>
                                <li><a href="/article/211.htm"
                                       title="linux 常用命令" target="_blank">linux 常用命令</a>
                                    <span class="text-muted">BlueSkator</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E5%91%BD%E4%BB%A4/1.htm">命令</a>
                                    <div>1.grep 
相信这个命令可以说是大家最常用的命令之一了。尤其是查询生产环境的日志,这个命令绝对是必不可少的。 
但之前总是习惯于使用 (grep -n 关键字 文件名 )查出关键字以及该关键字所在的行数,然后再用 (sed -n  '100,200p' 文件名),去查出该关键字之后的日志内容。 
但其实还有更简便的办法,就是用(grep  -B n、-A n、-C n 关键</div>
                                </li>
                                <li><a href="/article/338.htm"
                                       title="php heredoc原文档和nowdoc语法" target="_blank">php heredoc原文档和nowdoc语法</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a><a class="tag" taget="_blank" href="/search/heredoc/1.htm">heredoc</a><a class="tag" taget="_blank" href="/search/nowdoc/1.htm">nowdoc</a>
                                    <div><!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Current To-Do List</title>
</head>
<body>
<?</div>
                                </li>
                                <li><a href="/article/465.htm"
                                       title="overflow的属性" target="_blank">overflow的属性</a>
                                    <span class="text-muted">周华华</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml&q</div>
                                </li>
                                <li><a href="/article/592.htm"
                                       title="《我所了解的Java》——总体目录" target="_blank">《我所了解的Java》——总体目录</a>
                                    <span class="text-muted">g21121</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>        准备用一年左右时间写一个系列的文章《我所了解的Java》,目录及内容会不断完善及调整。 
        在编写相关内容时难免出现笔误、代码无法执行、名词理解错误等,请大家及时指出,我会第一时间更正。 
   &n</div>
                                </li>
                                <li><a href="/article/719.htm"
                                       title="[简单]docx4j常用方法小结" target="_blank">[简单]docx4j常用方法小结</a>
                                    <span class="text-muted">53873039oycg</span>
<a class="tag" taget="_blank" href="/search/docx/1.htm">docx</a>
                                    <div>        本代码基于docx4j-3.2.0,在office word 2007上测试通过。代码如下: 
         
import java.io.File;
import java.io.FileInputStream;
import ja</div>
                                </li>
                                <li><a href="/article/846.htm"
                                       title="Spring配置学习" target="_blank">Spring配置学习</a>
                                    <span class="text-muted">云端月影</span>
<a class="tag" taget="_blank" href="/search/spring%E9%85%8D%E7%BD%AE/1.htm">spring配置</a>
                                    <div> 
首先来看一个标准的Spring配置文件 applicationContext.xml 
 
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
 xmlns:xsi=&q</div>
                                </li>
                                <li><a href="/article/973.htm"
                                       title="Java新手入门的30个基本概念三" target="_blank">Java新手入门的30个基本概念三</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E6%96%B0%E6%89%8B/1.htm">新手</a><a class="tag" taget="_blank" href="/search/java+%E5%85%A5%E9%97%A8/1.htm">java 入门</a>
                                    <div>17.Java中的每一个类都是从Object类扩展而来的。  18.object类中的equal和toString方法。  equal用于测试一个对象是否同另一个对象相等。  toString返回一个代表该对象的字符串,几乎每一个类都会重载该方法,以便返回当前状态的正确表示.(toString 方法是一个很重要的方法)   19.通用编程:任何类类型的所有值都可以同object类性的变量来代替。 </div>
                                </li>
                                <li><a href="/article/1100.htm"
                                       title="《2008 IBM Rational 软件开发高峰论坛会议》小记" target="_blank">《2008 IBM Rational 软件开发高峰论坛会议》小记</a>
                                    <span class="text-muted">antonyup_2006</span>
<a class="tag" taget="_blank" href="/search/%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95/1.htm">软件测试</a><a class="tag" taget="_blank" href="/search/%E6%95%8F%E6%8D%B7%E5%BC%80%E5%8F%91/1.htm">敏捷开发</a><a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/1.htm">项目管理</a><a class="tag" taget="_blank" href="/search/IBM/1.htm">IBM</a><a class="tag" taget="_blank" href="/search/%E6%B4%BB%E5%8A%A8/1.htm">活动</a>
                                    <div>我一直想写些总结,用于交流和备忘,然都没提笔,今以一篇参加活动的感受小记开个头,呵呵! 
 
其实参加《2008 IBM Rational 软件开发高峰论坛会议》是9月4号,那天刚好调休.但接着项目颇为忙,所以今天在中秋佳节的假期里整理了下. 
 
参加这次活动是一个朋友给的一个邀请书,才知道有这样的一个活动,虽然现在项目暂时没用到IBM的解决方案,但觉的参与这样一个活动可以拓宽下视野和相关知识.</div>
                                </li>
                                <li><a href="/article/1227.htm"
                                       title="PL/SQL的过程编程,异常,声明变量,PL/SQL块" target="_blank">PL/SQL的过程编程,异常,声明变量,PL/SQL块</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/PL%2FSQL%E7%9A%84%E8%BF%87%E7%A8%8B%E7%BC%96%E7%A8%8B/1.htm">PL/SQL的过程编程</a><a class="tag" taget="_blank" href="/search/%E5%BC%82%E5%B8%B8/1.htm">异常</a><a class="tag" taget="_blank" href="/search/PL%2FSQL%E5%9D%97/1.htm">PL/SQL块</a><a class="tag" taget="_blank" href="/search/%E5%A3%B0%E6%98%8E%E5%8F%98%E9%87%8F/1.htm">声明变量</a>
                                    <div>PL/SQL; 
   
   过程;

    符号;

     变量;

     PL/SQL块;

     输出;

     异常;
 
  
  
PL/SQL 是过程语言(Procedural Language)与结构化查询语言(SQL)结合而成的编程语言PL/SQL 是对 SQL 的扩展,sql的执行时每次都要写操作</div>
                                </li>
                                <li><a href="/article/1354.htm"
                                       title="Mockito(三)--完整功能介绍" target="_blank">Mockito(三)--完整功能介绍</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/1.htm">持续集成</a><a class="tag" taget="_blank" href="/search/mockito/1.htm">mockito</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/1.htm">单元测试</a>
                                    <div>        mockito官网:http://code.google.com/p/mockito/,打开documentation可以看到官方最新的文档资料。 
一.使用mockito验证行为 
//首先要import Mockito
import static org.mockito.Mockito.*;

//mo</div>
                                </li>
                                <li><a href="/article/1481.htm"
                                       title="精通Oracle10编程SQL(8)使用复合数据类型" target="_blank">精通Oracle10编程SQL(8)使用复合数据类型</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/plsql/1.htm">plsql</a>
                                    <div>/*
 *使用复合数据类型
 */

--PL/SQL记录
--定义PL/SQL记录
--自定义PL/SQL记录
DECLARE
  TYPE emp_record_type IS RECORD(
     name emp.ename%TYPE,
     salary emp.sal%TYPE,
     dno emp.deptno%TYPE
  );
  emp_</div>
                                </li>
                                <li><a href="/article/1608.htm"
                                       title="【Linux常用命令一】grep命令" target="_blank">【Linux常用命令一】grep命令</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/Linux%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/1.htm">Linux常用命令</a>
                                    <div>grep命令格式 
  
grep [option] pattern [file-list] 
  
  
grep命令用于在指定的文件(一个或者多个,file-list)中查找包含模式串(pattern)的行,[option]用于控制grep命令的查找方式。 
  
pattern可以是普通字符串,也可以是正则表达式,当查找的字符串包含正则表达式字符或者特</div>
                                </li>
                                <li><a href="/article/1735.htm"
                                       title="mybatis3入门学习笔记" target="_blank">mybatis3入门学习笔记</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/ibatis/1.htm">ibatis</a><a class="tag" taget="_blank" href="/search/qq/1.htm">qq</a><a class="tag" taget="_blank" href="/search/jdbc/1.htm">jdbc</a><a class="tag" taget="_blank" href="/search/%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86/1.htm">配置管理</a>
                                    <div>MyBatis 的前身就是iBatis,是一个数据持久层(ORM)框架。  MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis对JDBC进行了一次很浅的封装。 
  
以前也学过iBatis,因为MyBatis是iBatis的升级版本,最初以为改动应该不大,实际结果是MyBatis对配置文件进行了一些大的改动,使整个框架更加方便人性化。</div>
                                </li>
                                <li><a href="/article/1862.htm"
                                       title="Linux 命令神器:lsof 入门" target="_blank">Linux 命令神器:lsof 入门</a>
                                    <span class="text-muted">ronin47</span>
<a class="tag" taget="_blank" href="/search/lsof/1.htm">lsof</a>
                                    <div>       
lsof是系统管理/安全的尤伯工具。我大多数时候用它来从系统获得与网络连接相关的信息,但那只是这个强大而又鲜为人知的应用的第一步。将这个工具称之为lsof真实名副其实,因为它是指“列出打开文件(lists openfiles)”。而有一点要切记,在Unix中一切(包括网络套接口)都是文件。 
有趣的是,lsof也是有着最多</div>
                                </li>
                                <li><a href="/article/1989.htm"
                                       title="java实现两个大数相加,可能存在溢出。" target="_blank">java实现两个大数相加,可能存在溢出。</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java%E5%AE%9E%E7%8E%B0/1.htm">java实现</a>
                                    <div>
import java.math.BigInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class BigIntegerAddition {

	/**
	 * 题目:java实现两个大数相加,可能存在溢出。
	 * 如123456789 + 987654321</div>
                                </li>
                                <li><a href="/article/2116.htm"
                                       title="Kettle学习资料分享,附大神用Kettle的一套流程完成对整个数据库迁移方法" target="_blank">Kettle学习资料分享,附大神用Kettle的一套流程完成对整个数据库迁移方法</a>
                                    <span class="text-muted">Kai_Ge</span>
<a class="tag" taget="_blank" href="/search/Kettle/1.htm">Kettle</a>
                                    <div>Kettle学习资料分享 
  
Kettle 3.2 使用说明书 
目录 
概述..........................................................................................................................................7 
1.Kettle 资源库管</div>
                                </li>
                                <li><a href="/article/2243.htm"
                                       title="[货币与金融]钢之炼金术士" target="_blank">[货币与金融]钢之炼金术士</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E9%87%91%E8%9E%8D/1.htm">金融</a>
                                    <div> 
 
       自古以来,都有一些人在从事炼金术的工作.........但是很少有成功的 
 
       那么随着人类在理论物理和工程物理上面取得的一些突破性进展...... 
 
       炼金术这个古老</div>
                                </li>
                                <li><a href="/article/2370.htm"
                                       title="Toast原来也可以多样化" target="_blank">Toast原来也可以多样化</a>
                                    <span class="text-muted">dai_lm</span>
<a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/toast/1.htm">toast</a>
                                    <div>Style 1: 默认 
 

Toast def = Toast.makeText(this, "default", Toast.LENGTH_SHORT);
def.show();
 
Style 2: 顶部显示 
 

Toast top = Toast.makeText(this, "top", Toast.LENGTH_SHORT);
t</div>
                                </li>
                                <li><a href="/article/2497.htm"
                                       title="java数据计算的几种解决方法3" target="_blank">java数据计算的几种解决方法3</a>
                                    <span class="text-muted">datamachine</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a><a class="tag" taget="_blank" href="/search/ibatis/1.htm">ibatis</a><a class="tag" taget="_blank" href="/search/r-langue/1.htm">r-langue</a><a class="tag" taget="_blank" href="/search/r/1.htm">r</a>
                                    <div>4、iBatis 
    简单敏捷因此强大的数据计算层。和Hibernate不同,它鼓励写SQL,所以学习成本最低。同时它用最小的代价实现了计算脚本和JAVA代码的解耦,只用20%的代价就实现了hibernate 80%的功能,没实现的20%是计算脚本和数据库的解耦。 
    复杂计算环境是它的弱项,比如:分布式计算、复杂计算、非数据</div>
                                </li>
                                <li><a href="/article/2624.htm"
                                       title="向网页中插入透明Flash的方法和技巧" target="_blank">向网页中插入透明Flash的方法和技巧</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/Flash/1.htm">Flash</a>
                                    <div>将 
Flash 作品插入网页的时候,我们有时候会需要将它设为透明,有时候我们需要在Flash的背面插入一些漂亮的图片,搭配出漂亮的效果……下面我们介绍一些将Flash插入网页中的一些透明的设置技巧。  
  一、Swf透明、无坐标控制  首先教大家最简单的插入Flash的代码,透明,无坐标控制:   注意wmode="transparent"是控制Flash是否透明</div>
                                </li>
                                <li><a href="/article/2751.htm"
                                       title="ios UICollectionView的使用" target="_blank">ios UICollectionView的使用</a>
                                    <span class="text-muted">dcj3sjt126com</span>

                                    <div>UICollectionView的使用有两种方法,一种是继承UICollectionViewController,这个Controller会自带一个UICollectionView;另外一种是作为一个视图放在普通的UIViewController里面。 
个人更喜欢第二种。下面采用第二种方式简单介绍一下UICollectionView的使用。 
1.UIViewController实现委托,代码如</div>
                                </li>
                                <li><a href="/article/2878.htm"
                                       title="Eos平台java公共逻辑" target="_blank">Eos平台java公共逻辑</a>
                                    <span class="text-muted">蕃薯耀</span>
<a class="tag" taget="_blank" href="/search/Eos%E5%B9%B3%E5%8F%B0java%E5%85%AC%E5%85%B1%E9%80%BB%E8%BE%91/1.htm">Eos平台java公共逻辑</a><a class="tag" taget="_blank" href="/search/Eos%E5%B9%B3%E5%8F%B0/1.htm">Eos平台</a><a class="tag" taget="_blank" href="/search/java%E5%85%AC%E5%85%B1%E9%80%BB%E8%BE%91/1.htm">java公共逻辑</a>
                                    <div> Eos平台java公共逻辑 
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
蕃薯耀 2015年6月1日 17:20:4</div>
                                </li>
                                <li><a href="/article/3005.htm"
                                       title="SpringMVC4零配置--Web上下文配置【MvcConfig】" target="_blank">SpringMVC4零配置--Web上下文配置【MvcConfig】</a>
                                    <span class="text-muted">hanqunfeng</span>
<a class="tag" taget="_blank" href="/search/springmvc4/1.htm">springmvc4</a>
                                    <div>与SpringSecurity的配置类似,spring同样为我们提供了一个实现类WebMvcConfigurationSupport和一个注解@EnableWebMvc以帮助我们减少bean的声明。 
  
applicationContext-MvcConfig.xml 
<!-- 启用注解,并定义组件查找规则 ,mvc层只负责扫描@Controller -->
	<</div>
                                </li>
                                <li><a href="/article/3132.htm"
                                       title="解决ie和其他浏览器poi下载excel文件名乱码" target="_blank">解决ie和其他浏览器poi下载excel文件名乱码</a>
                                    <span class="text-muted">jackyrong</span>
<a class="tag" taget="_blank" href="/search/Excel/1.htm">Excel</a>
                                    <div>   使用poi,做传统的excel导出,然后想在浏览器中,让用户选择另存为,保存用户下载的xls文件,这个时候,可能的是在ie下出现乱码(ie,9,10,11),但在firefox,chrome下没乱码, 
 
因此必须综合判断,编写一个工具类: 
 
 
     

/**
     * 
     * @Title: pro</div>
                                </li>
                                <li><a href="/article/3259.htm"
                                       title="挥洒泪水的青春" target="_blank">挥洒泪水的青春</a>
                                    <span class="text-muted">lampcy</span>
<a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a><a class="tag" taget="_blank" href="/search/%E7%94%9F%E6%B4%BB/1.htm">生活</a><a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E5%91%98/1.htm">程序员</a>
                                    <div>2015年2月28日,我辞职了,离开了相处一年的触控,转过身--挥洒掉泪水,毅然来到了兄弟连,背负着许多的不解、质疑——”你一个零基础、脑子又不聪明的人,还敢跨行业,选择Unity3D?“,”真是不自量力••••••“,”真是初生牛犊不怕虎•••••“,••••••我只是淡淡一笑,拎着行李----坐上了通向挥洒泪水的青春之地——兄弟连! 
这就是我青春的分割线,不后悔,只会去用泪水浇灌——已经来到</div>
                                </li>
                                <li><a href="/article/3386.htm"
                                       title="稳增长之中国股市两点意见-----严控做空,建立涨跌停版停牌重组机制" target="_blank">稳增长之中国股市两点意见-----严控做空,建立涨跌停版停牌重组机制</a>
                                    <span class="text-muted">nannan408</span>

                                    <div>   对于股市,我们国家的监管还是有点拼的,但始终拼不过飞流直下的恐慌,为什么呢? 
   笔者首先支持股市的监管。对于股市越管越荡的现象,笔者认为首先是做空力量超过了股市自身的升力,并且对于跌停停牌重组的快速反应还没建立好,上市公司对于股价下跌没有很好的利好支撑。 
   我们来看美国和香港是怎么应对股灾的。美国是靠禁止重要股票做空,在</div>
                                </li>
                                <li><a href="/article/3513.htm"
                                       title="动态设置iframe高度(iframe高度自适应)" target="_blank">动态设置iframe高度(iframe高度自适应)</a>
                                    <span class="text-muted">Rainbow702</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/iframe/1.htm">iframe</a><a class="tag" taget="_blank" href="/search/contentDocument/1.htm">contentDocument</a><a class="tag" taget="_blank" href="/search/%E9%AB%98%E5%BA%A6%E8%87%AA%E9%80%82%E5%BA%94/1.htm">高度自适应</a><a class="tag" taget="_blank" href="/search/%E5%B1%80%E9%83%A8%E5%88%B7%E6%96%B0/1.htm">局部刷新</a>
                                    <div>如果需要对画面中的部分区域作局部刷新,大家可能都会想到使用ajax。 
但有些情况下,须使用在页面中嵌入一个iframe来作局部刷新。 
对于使用iframe的情况,发现有一个问题,就是iframe中的页面的高度可能会很高,但是外面页面并不会被iframe内部页面给撑开,如下面的结构: 
<div id="content">
    <div id=&quo</div>
                                </li>
                                <li><a href="/article/3640.htm"
                                       title="用Rapael做图表" target="_blank">用Rapael做图表</a>
                                    <span class="text-muted">tntxia</span>
<a class="tag" taget="_blank" href="/search/rap/1.htm">rap</a>
                                    <div>function drawReport(paper,attr,data){ 
     
    var width = attr.width; 
    var height = attr.height; 
     
    var max = 0; 
  &nbs</div>
                                </li>
                                <li><a href="/article/3767.htm"
                                       title="HTML5 bootstrap2网页兼容(支持IE10以下)" target="_blank">HTML5 bootstrap2网页兼容(支持IE10以下)</a>
                                    <span class="text-muted">xiaoluode</span>
<a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/bootstrap/1.htm">bootstrap</a>
                                    <div><!DOCTYPE html>
<html>
<head lang="zh-CN">
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge"></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>