20. async 函数

仅为方便个人查询使用
来源:http://es6.ruanyifeng.com/#docs/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());
};

上面代码的函数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命令的语法糖。

基本用法

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

f().then(v => console.log(v))
// 123
</code></pre> 
 <p>上面代码中,<code>await</code>命令的参数是数值<code>123</code>,这时等同于<code>return 123</code>。</p> 
 <p>另一种情况是,<code>await</code>命令后面是一个<code>thenable</code>对象(即定义<code>then</code>方法的对象),那么<code>await</code>会将其等同于 Promise 对象。</p> 
 <pre><code class="javascript">class Sleep {
  constructor(timeout) {
    this.timeout = timeout;
  }
  then(resolve, reject) {
    const startTime = Date.now();
    setTimeout(
      () => resolve(Date.now() - startTime),
      this.timeout
    );
  }
}

(async () => {
  const sleepTime = await new Sleep(1000);
  console.log(sleepTime);
})();
// 1000
</code></pre> 
 <p>上面代码中,<code>await</code>命令后面是一个<code>Sleep</code>对象的实例。这个实例不是 Promise 对象,但是因为定义了<code>then</code>方法,<code>await</code>会将其视为<code>Promise</code>处理。</p> 
 <p>这个例子还演示了如何实现休眠效果。JavaScript 一直没有休眠的语法,但是借助<code>await</code>命令就可以让程序停顿指定的时间。下面给出了一个简化的<code>sleep</code>实现。</p> 
 <pre><code class="javascript">function sleep(interval) {
  return new Promise(resolve => {
    setTimeout(resolve, interval);
  })
}

// 用法
async function one2FiveInAsync() {
  for(let i = 1; i <= 5; i++) {
    console.log(i);
    await sleep(1000);
  }
}

one2FiveInAsync();
</code></pre> 
 <p><code>await</code>命令后面的 Promise 对象如果变为<code>reject</code>状态,则<code>reject</code>的参数会被<code>catch</code>方法的回调函数接收到。</p> 
 <pre><code class="javascript">async function f() {
  await Promise.reject('出错了');
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了
</code></pre> 
 <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> 
 <pre><code class="javascript">async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}
</code></pre> 
 <p>上面代码中,第二个<code>await</code>语句是不会执行的,因为第一个<code>await</code>语句状态变成了<code>reject</code>。</p> 
 <p>有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个<code>await</code>放在<code>try...catch</code>结构里面,这样不管这个异步操作是否成功,第二个<code>await</code>都会执行。</p> 
 <pre><code class="javascript">async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

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

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

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// Error:出错了
</code></pre> 
 <p>上面代码中,<code>async</code>函数<code>f</code>执行后,<code>await</code>后面的 Promise 对象会抛出一个错误对象,导致<code>catch</code>方法的回调函数被调用,它的参数就是抛出的错误对象。具体的执行机制,可以参考后文的“async 函数的实现原理”。</p> 
 <p>防止出错的方法,也是将其放在<code>try...catch</code>代码块之中。</p> 
 <pre><code class="javascript">async function f() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('出错了');
    });
  } catch(e) {
  }
  return await('hello world');
}
</code></pre> 
 <p>如果有多个<code>await</code>命令,可以统一放在<code>try...catch</code>结构中。</p> 
 <pre><code class="javascript">async function main() {
  try {
    const val1 = await firstStep();
    const val2 = await secondStep(val1);
    const val3 = await thirdStep(val1, val2);

    console.log('Final: ', val3);
  }
  catch (err) {
    console.error(err);
  }
}
</code></pre> 
 <p>下面的例子使用<code>try...catch</code>结构,实现多次重复尝试。</p> 
 <pre><code class="javascript">const superagent = require('superagent');
const NUM_RETRIES = 3;

async function test() {
  let i;
  for (i = 0; i < NUM_RETRIES; ++i) {
    try {
      await superagent.get('http://google.com/this-throws-an-error');
      break;
    } catch(err) {}
  }
  console.log(i); // 3
}

test();
</code></pre> 
 <p>上面代码中,如果<code>await</code>操作成功,就会使用<code>break</code>语句退出循环;如果失败,会被<code>catch</code>语句捕捉,然后进入下一轮循环。</p> 
 <h3>使用注意点</h3> 
 <p>第一点,前面已经说过,<code>await</code>命令后面的<code>Promise</code>对象,运行结果可能是<code>rejected</code>,所以最好把<code>await</code>命令放在<code>try...catch</code>代码块中。</p> 
 <pre><code class="javascript">async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一种写法

async function myFunction() {
  await somethingThatReturnsAPromise()
  .catch(function (err) {
    console.log(err);
  });
}
</code></pre> 
 <p>第二点,多个<code>await</code>命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。</p> 
 <pre><code class="javascript">let foo = await getFoo();
let bar = await getBar();
</code></pre> 
 <p>上面代码中,<code>getFoo</code>和<code>getBar</code>是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有<code>getFoo</code>完成以后,才会执行<code>getBar</code>,完全可以让它们同时触发。</p> 
 <pre><code class="javascript">// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

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

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

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

  for (let doc of docs) {
    await db.post(doc);
  }
}
</code></pre> 
 <p>如果确实希望多个请求并发执行,可以使用<code>Promise.all</code>方法。当三个请求都会<code>resolved</code>时,下面两种写法效果相同。</p> 
 <pre><code class="javascript">async function dbFuc(db) {
  let docs = [{}, {}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = await Promise.all(promises);
  console.log(results);
}

// 或者使用下面的写法

async function dbFuc(db) {
  let docs = [{}, {}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = [];
  for (let promise of promises) {
    results.push(await promise);
  }
  console.log(results);
}
</code></pre> 
 <p>第四点,async 函数可以保留运行堆栈。</p> 
 <pre><code class="javascript">const a = () => {
  b().then(() => c());
};
</code></pre> 
 <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> 
 <pre><code class="javascript">const a = async () => {
  await b();
  c();
};
</code></pre> 
 <p>上面代码中,<code>b()</code>运行的时候,<code>a()</code>是暂停执行,上下文环境都保存着。一旦<code>b()</code>或<code>c()</code>报错,错误堆栈将包括<code>a()</code>。</p> 
 <h2>async 函数的实现原理</h2> 
 <p>async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。</p> 
 <pre><code class="javascript">async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}
</code></pre> 
 <p>所有的<code>async</code>函数都可以写成上面的第二种形式,其中的<code>spawn</code>函数就是自动执行器。</p> 
 <p>下面给出<code>spawn</code>函数的实现,基本就是前文自动执行器的翻版。</p> 
 <pre><code class="javascript">function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}
</code></pre> 
 <h2>与其他异步处理方法的比较</h2> 
 <p>我们通过一个例子,来看 async 函数与 Promise、Generator 函数的比较。</p> 
 <p>假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。</p> 
 <p>首先是 Promise 的写法。</p> 
 <pre><code class="javascript">function chainAnimationsPromise(elem, animations) {

  // 变量ret用来保存上一个动画的返回值
  let ret = null;

  // 新建一个空的Promise
  let p = Promise.resolve();

  // 使用then方法,添加所有动画
  for(let anim of animations) {
    p = p.then(function(val) {
      ret = val;
      return anim(elem);
    });
  }

  // 返回一个部署了错误捕捉机制的Promise
  return p.catch(function(e) {
    /* 忽略错误,继续执行 */
  }).then(function() {
    return ret;
  });

}
</code></pre> 
 <p>虽然 Promise 的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(<code>then</code>、<code>catch</code>等等),操作本身的语义反而不容易看出来。</p> 
 <p>接着是 Generator 函数的写法。</p> 
 <pre><code class="javascript">function chainAnimationsGenerator(elem, animations) {

  return spawn(function*() {
    let ret = null;
    try {
      for(let anim of animations) {
        ret = yield anim(elem);
      }
    } catch(e) {
      /* 忽略错误,继续执行 */
    }
    return ret;
  });

}
</code></pre> 
 <p>上面代码使用 Generator 函数遍历了每个动画,语义比 Promise 写法更清晰,用户定义的操作全部都出现在<code>spawn</code>函数的内部。这个写法的问题在于,必须有一个任务运行器,自动执行 Generator 函数,上面代码的<code>spawn</code>函数就是自动执行器,它返回一个 Promise 对象,而且必须保证<code>yield</code>语句后面的表达式,必须返回一个 Promise。</p> 
 <p>最后是 async 函数的写法。</p> 
 <pre><code class="javascript">async function chainAnimationsAsync(elem, animations) {
  let ret = null;
  try {
    for(let anim of animations) {
      ret = await anim(elem);
    }
  } catch(e) {
    /* 忽略错误,继续执行 */
  }
  return ret;
}
</code></pre> 
 <p>可以看到 Async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。它将 Generator 写法中的自动执行器,改在语言层面提供,不暴露给用户,因此代码量最少。如果使用 Generator 写法,自动执行器需要用户自己提供。</p> 
 <h2>实例:按顺序完成异步操作</h2> 
 <p>实际开发中,经常遇到一组异步操作,需要按照顺序完成。比如,依次远程读取一组 URL,然后按照读取的顺序输出结果。</p> 
 <p>Promise 的写法如下。</p> 
 <pre><code class="javascript">function logInOrder(urls) {
  // 远程读取所有URL
  const textPromises = urls.map(url => {
    return fetch(url).then(response => response.text());
  });

  // 按次序输出
  textPromises.reduce((chain, textPromise) => {
    return chain.then(() => textPromise)
      .then(text => console.log(text));
  }, Promise.resolve());
}
</code></pre> 
 <p>上面代码使用<code>fetch</code>方法,同时远程读取一组 URL。每个<code>fetch</code>操作都返回一个 Promise 对象,放入<code>textPromises</code>数组。然后,<code>reduce</code>方法依次处理每个 Promise 对象,然后使用<code>then</code>,将所有 Promise 对象连起来,因此就可以依次输出结果。</p> 
 <p>这种写法不太直观,可读性比较差。下面是 async 函数实现。</p> 
 <pre><code class="javascript">async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}
</code></pre> 
 <p>上面代码确实大大简化,问题是所有远程操作都是继发。只有前一个 URL 返回结果,才会去读取下一个 URL,这样做效率很差,非常浪费时间。我们需要的是并发发出远程请求。</p> 
 <pre><code class="javascript">async function logInOrder(urls) {
  // 并发读取远程URL
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  // 按次序输出
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}
</code></pre> 
 <p>上面代码中,虽然<code>map</code>方法的参数是<code>async</code>函数,但它是并发执行的,因为只有<code>async</code>函数内部是继发执行,外部不受影响。后面的<code>for..of</code>循环内部使用了<code>await</code>,因此实现了按顺序输出。</p> 
 <h2>顶层 await</h2> 
 <p>根据语法规格,<code>await</code>命令只能出现在 async 函数内部,否则都会报错。</p> 
 <pre><code class="javascript">// 报错
const data = await fetch('https://api.example.com');
</code></pre> 
 <p>上面代码中,<code>await</code>命令独立使用,没有放在 async 函数里面,就会报错。</p> 
 <p>目前,有一个语法提案,允许在模块的顶层独立使用<code>await</code>命令。这个提案的目的,是借用<code>await</code>解决模块异步加载的问题。</p> 
 <pre><code class="javascript">// awaiting.js
let output;
async function main() {
  const dynamic = await import(someMission);
  const data = await fetch(url);
  output = someProcess(dynamic.default, data);
}
main();
export { output };
</code></pre> 
 <p>上面代码中,模块<code>awaiting.js</code>的输出值<code>output</code>,取决于异步操作。我们把异步操作包装在一个 async 函数里面,然后调用这个函数,只有等里面的异步操作都执行,变量<code>output</code>才会有值,否则就返回<code>undefined</code>。</p> 
 <p>上面的代码也可以写成立即执行函数的形式。</p> 
 <pre><code class="javascript">// awaiting.js
let output;
(async function main() {
  const dynamic = await import(someMission);
  const data = await fetch(url);
  output = someProcess(dynamic.default, data);
})();
export { output };
</code></pre> 
 <p>下面是加载这个模块的写法。</p> 
 <pre><code class="javascript">// usage.js
import { output } from "./awaiting.js";

function outputPlusValue(value) { return output + value }

console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);
</code></pre> 
 <p>上面代码中,<code>outputPlusValue()</code>的执行结果,完全取决于执行的时间。如果<code>awaiting.js</code>里面的异步操作没执行完,加载进来的<code>output</code>的值就是<code>undefined</code>。</p> 
 <p>目前的解决方法,就是让原始模块输出一个 Promise 对象,从这个 Promise 对象判断异步操作有没有结束。</p> 
 <pre><code class="javascript">// awaiting.js
let output;
export default (async function main() {
  const dynamic = await import(someMission);
  const data = await fetch(url);
  output = someProcess(dynamic.default, data);
})();
export { output };
</code></pre> 
 <p>上面代码中,<code>awaiting.js</code>除了输出<code>output</code>,还默认输出一个 Promise 对象(async 函数立即执行后,返回一个 Promise 对象),从这个对象判断异步操作是否结束。</p> 
 <p>下面是加载这个模块的新的写法。</p> 
 <pre><code class="javascript">// usage.js
import promise, { output } from "./awaiting.js";

function outputPlusValue(value) { return output + value }

promise.then(() => {
  console.log(outputPlusValue(100));
  setTimeout(() => console.log(outputPlusValue(100), 1000);
});
</code></pre> 
 <p>上面代码中,将<code>awaiting.js</code>对象的输出,放在<code>promise.then()</code>里面,这样就能保证异步操作完成以后,才去读取<code>output</code>。</p> 
 <p>这种写法比较麻烦,等于要求模块的使用者遵守一个额外的使用协议,按照特殊的方法使用这个模块。一旦你忘了要用 Promise 加载,只使用正常的加载方法,依赖这个模块的代码就可能出错。而且,如果上面的<code>usage.js</code>又有对外的输出,等于这个依赖链的所有模块都要使用 Promise 加载。</p> 
 <p>顶层的<code>await</code>命令,就是为了解决这个问题。它保证只有异步操作完成,模块才会输出值。</p> 
 <pre><code class="javascript">// awaiting.js
const dynamic = import(someMission);
const data = fetch(url);
export const output = someProcess((await dynamic).default, await data);
</code></pre> 
 <p>上面代码中,两个异步操作在输出的时候,都加上了<code>await</code>命令。只有等到异步操作完成,这个模块才会输出值。</p> 
 <p>加载这个模块的写法如下。</p> 
 <pre><code class="javascript">// usage.js
import { output } from "./awaiting.js";
function outputPlusValue(value) { return output + value }

console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);
</code></pre> 
 <p>上面代码的写法,与普通的模块加载完全一样。也就是说,模块的使用者完全不用关心,依赖模块的内部有没有异步操作,正常加载即可。</p> 
 <p>这时,模块的加载会等待依赖模块(上例是<code>awaiting.js</code>)的异步操作完成,才执行后面的代码,有点像暂停在那里。所以,它总是会得到正确的<code>output</code>,不会因为加载时机的不同,而得到不一样的值。</p> 
 <p>下面是顶层<code>await</code>的一些使用场景。</p> 
 <pre><code class="javascript">// import() 方法加载
const strings = await import(`/i18n/${navigator.language}`);

// 数据库操作
const connection = await dbConnector();

// 依赖回滚
let jQuery;
try {
  jQuery = await import('https://cdn-a.com/jQuery');
} catch {
  jQuery = await import('https://cdn-b.com/jQuery');
}
</code></pre> 
 <p>注意,如果加载多个包含顶层<code>await</code>命令的模块,加载命令是同步执行的。</p> 
 <pre><code class="javascript">// x.js
console.log("X1");
await new Promise(r => setTimeout(r, 1000));
console.log("X2");

// y.js
console.log("Y");

// z.js
import "./x.js";
import "./y.js";
console.log("Z");
</code></pre> 
 <p>上面代码有三个模块,最后的<code>z.js</code>加载<code>x.js</code>和<code>y.js</code>,打印结果是<code>X1</code>、<code>Y</code>、<code>X2</code>、<code>Z</code>。这说明,<code>z.js</code>并没有等待<code>x.js</code>加载完成,再去加载<code>y.js</code>。</p> 
 <p>顶层的<code>await</code>命令有点像,交出代码的执行权给其他的模块加载,等异步操作完成后,再拿回执行权,继续向下执行。</p> 
</article>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1406663560620244992"></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">你可能感兴趣的:(20. async 函数)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1950232190038110208.htm"
                           title="day15|前端框架学习和算法" target="_blank">day15|前端框架学习和算法</a>
                        <span class="text-muted">universe_01</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a>
                        <div>T22括号生成先把所有情况都画出来,然后(在满足什么情况下)把不符合条件的删除。T78子集要画树状图,把思路清晰。可以用暴力法、回溯法和DFS做这个题DFS深度搜索:每个边都走完,再回溯应用:二叉树搜索,图搜索回溯算法=DFS+剪枝T200岛屿数量(非常经典BFS宽度把树状转化成队列形式,lambda匿名函数“一次性的小函数,没有名字”setup语法糖:让代码更简洁好写的语法ref创建:基本类型的</div>
                    </li>
                    <li><a href="/article/1950225785054883840.htm"
                           title="Java | 多线程经典问题 - 售票" target="_blank">Java | 多线程经典问题 - 售票</a>
                        <span class="text-muted">Ada54</span>

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

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

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

                        <div>Day10–栈与队列–232.用栈实现队列,225.用队列实现栈,20.有效的括号,1047.删除字符串中的所有相邻重复项,150.逆波兰表达式求值232.用栈实现队列思路:用ArrayDeque来实现;有趣的dumpToStackOut();classMyQueue{DequestackIn;DequestackOut;publicMyQueue(){stackIn=newArrayDeque(</div>
                    </li>
                    <li><a href="/article/1950138760230400000.htm"
                           title="C语言基础-数组和指针的区别" target="_blank">C语言基础-数组和指针的区别</a>
                        <span class="text-muted">阿部春光</span>
<a class="tag" taget="_blank" href="/search/C%E8%AF%AD%E8%A8%80/1.htm">C语言</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>在C语言中,数组和指针是两个密切相关但又有显著区别的概念。下面我会详细解释它们之间的区别和联系。区别数组和指针在C语言中虽然经常一起使用,但它们是两个不同的概念,具有一些关键的区别:本质不同:数组:数组是一种数据结构,用于存储固定数量的同类型元素的连续内存块。数组名在某些上下文中(如取地址操作或sizeof操作符)代表整个数组,但在其他上下文中(如作为函数参数或用于指针算术)通常退化为指向数组第一</div>
                    </li>
                    <li><a href="/article/1950137599754563584.htm"
                           title="xgboost原理" target="_blank">xgboost原理</a>
                        <span class="text-muted">茶尽</span>

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

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

                        <div>之前的案例都是列数及行数相同,那如果是不同的情况下,该如何处理呢?原表:同行不同列原表目标表:同行不同列目标表此时我们可以通过另外一个分组函数来进行处理。Table.GroupTable.Group(tableastable,keyasany,aggregatedColumnsaslist,optionalgroupKindasnullablenumber,optionalcomparerasnu</div>
                    </li>
                    <li><a href="/article/1950078131993899008.htm"
                           title="C#返回两个数组或多个数组,函数方法的一种写法" target="_blank">C#返回两个数组或多个数组,函数方法的一种写法</a>
                        <span class="text-muted">zhannghong2003</span>
<a class="tag" taget="_blank" href="/search/C%23/1.htm">C#</a><a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a>
                        <div>public(float[]x,float[]y)GetPolygonCollider2DPoints(PolygonCollider2Dobjects){Vector2[]points=objects.points;float[]x=newfloat[points.Length];float[]y=newfloat[points.Length];for(inti=0;i<points.Lengt</div>
                    </li>
                    <li><a href="/article/1950074471545106432.htm"
                           title="C++-coroutines协程 将自定义类型转为awaitable(可等待)类型的两种方法" target="_blank">C++-coroutines协程 将自定义类型转为awaitable(可等待)类型的两种方法</a>
                        <span class="text-muted">mrbone11</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Coroutines/1.htm">Coroutines</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%8D%8F%E7%A8%8B/1.htm">协程</a><a class="tag" taget="_blank" href="/search/coroutines/1.htm">coroutines</a>
                        <div>文章目录前言重载operatorco_await定义promise_type::await_transform函数前言阅读本篇文章时,已经假定你有对协程的基本概念,如果没有,可以阅读我写的这篇协程入门文章我们已经知道,对于co_await\,expression的返回值必须是awaitable类型,即正确实现了如下三个函数:await_readyawait_suspendawait_resume对</div>
                    </li>
                    <li><a href="/article/1950068796244946944.htm"
                           title="Qt技术面试问题总结" target="_blank">Qt技术面试问题总结</a>
                        <span class="text-muted">查理_Erik</span>
<a class="tag" taget="_blank" href="/search/qt/1.htm">qt</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>1.你知道什么是双指针吗,有没有在项目上用过,什么情况下使用双指针?答:就是两级指针,一级指针指向对象的地址,两级指针指向一级指针的地址,一般用于指针的传参,链表操作,两维数组。2.假设我在一个函数外定义了一个空指针,把它作为形参传入到函数中,我在函数中使用这个指针new了一个内存,请问这段运行这段代码会出现什么后果?那我应该如何传入一个空指针形参进函数并正确new一个内存呢?答:使用二级指针,或</div>
                    </li>
                                <li><a href="/article/10.htm"
                                       title="jsonp 常用util方法" target="_blank">jsonp 常用util方法</a>
                                    <span class="text-muted">hw1287789687</span>
<a class="tag" taget="_blank" href="/search/jsonp/1.htm">jsonp</a><a class="tag" taget="_blank" href="/search/jsonp%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95/1.htm">jsonp常用方法</a><a class="tag" taget="_blank" href="/search/jsonp+callback/1.htm">jsonp callback</a>
                                    <div>jsonp 常用java方法 
(1)以jsonp的形式返回:函数名(json字符串) 
/***
	 * 用于jsonp调用
	 * @param map : 用于构造json数据
	 * @param callback : 回调的javascript方法名
	 * @param filters : <code>SimpleBeanPropertyFilter theFilt</div>
                                </li>
                                <li><a href="/article/137.htm"
                                       title="多线程场景" target="_blank">多线程场景</a>
                                    <span class="text-muted">alafqq</span>
<a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">多线程</a>
                                    <div>0 
能不能简单描述一下你在java web开发中需要用到多线程编程的场景?0 
对多线程有些了解,但是不太清楚具体的应用场景,能简单说一下你遇到的多线程编程的场景吗? 
Java多线程 
2012年11月23日 15:41 Young9007 Young9007 
4 
0 0 4 
 
Comment添加评论关注(2) 
3个答案 按时间排序 按投票排序 
 
0 
0 
最典型的如: 
1、</div>
                                </li>
                                <li><a href="/article/264.htm"
                                       title="Maven学习——修改Maven的本地仓库路径" target="_blank">Maven学习——修改Maven的本地仓库路径</a>
                                    <span class="text-muted">Kai_Ge</span>
<a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a>
                                    <div>      安装Maven后我们会在用户目录下发现.m2 文件夹。默认情况下,该文件夹下放置了Maven本地仓库.m2/repository。所有的Maven构件(artifact)都被存储到该仓库中,以方便重用。但是windows用户的操作系统都安装在C盘,把Maven仓库放到C盘是很危险的,为此我们需要修改Maven的本地仓库路径。 
  
 </div>
                                </li>
                                <li><a href="/article/391.htm"
                                       title="placeholder的浏览器兼容" target="_blank">placeholder的浏览器兼容</a>
                                    <span class="text-muted">120153216</span>
<a class="tag" taget="_blank" href="/search/placeholder/1.htm">placeholder</a>
                                    <div>【前言】 
自从html5引入placeholder后,问题就来了, 
不支持html5的浏览器也先有这样的效果, 
各种兼容,之前考虑,今天测试人员逮住不放, 
想了个解决办法,看样子还行,记录一下。 
  
【原理】 
不使用placeholder,而是模拟placeholder的效果, 
大概就是用focus和focusout效果。 
  
【代码】 
<scrip</div>
                                </li>
                                <li><a href="/article/518.htm"
                                       title="debian_用iso文件创建本地apt源" target="_blank">debian_用iso文件创建本地apt源</a>
                                    <span class="text-muted">2002wmj</span>
<a class="tag" taget="_blank" href="/search/Debian/1.htm">Debian</a>
                                    <div>1.将N个debian-506-amd64-DVD-N.iso存放于本地或其他媒介内,本例是放在本机/iso/目录下

2.创建N个挂载点目录  
如下: 
debian:~#mkdir –r /media/dvd1 
debian:~#mkdir –r /media/dvd2 
debian:~#mkdir –r /media/dvd3 
…. 
debian:~#mkdir –r /media</div>
                                </li>
                                <li><a href="/article/645.htm"
                                       title="SQLSERVER耗时最长的SQL" target="_blank">SQLSERVER耗时最长的SQL</a>
                                    <span class="text-muted">357029540</span>
<a class="tag" taget="_blank" href="/search/SQL+Server/1.htm">SQL Server</a>
                                    <div>对于DBA来说,经常要知道存储过程的某些信息: 
 
1.   执行了多少次 
 
2.   执行的执行计划如何 
 
3.   执行的平均读写如何 
 
4.   执行平均需要多少时间 
 
列名          &</div>
                                </li>
                                <li><a href="/article/772.htm"
                                       title="com/genuitec/eclipse/j2eedt/core/J2EEProjectUtil" target="_blank">com/genuitec/eclipse/j2eedt/core/J2EEProjectUtil</a>
                                    <span class="text-muted">7454103</span>
<a class="tag" taget="_blank" href="/search/eclipse/1.htm">eclipse</a>
                                    <div>今天eclipse突然报了com/genuitec/eclipse/j2eedt/core/J2EEProjectUtil 错误,并且工程文件打不开了,在网上找了一下资料,然后按照方法操作了一遍,好了,解决方法如下: 
 
错误提示信息: 
 
An error has occurred.See error log for more details. 
Reason: 
com/genuitec/</div>
                                </li>
                                <li><a href="/article/899.htm"
                                       title="用正则删除文本中的html标签" target="_blank">用正则删除文本中的html标签</a>
                                    <span class="text-muted">adminjun</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1.htm">正则表达式</a><a class="tag" taget="_blank" href="/search/%E5%8E%BB%E6%8E%89html%E6%A0%87%E7%AD%BE/1.htm">去掉html标签</a>
                                    <div>使用文本编辑器录入文章存入数据中的文本是HTML标签格式,由于业务需要对HTML标签进行去除只保留纯净的文本内容,于是乎Java实现自动过滤。 
如下: 
public static String Html2Text(String inputString) {  
String htmlStr = inputString; // 含html标签的字符串
  String textSt</div>
                                </li>
                                <li><a href="/article/1026.htm"
                                       title="嵌入式系统设计中常用总线和接口" target="_blank">嵌入式系统设计中常用总线和接口</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/linux+%E5%9F%BA%E7%A1%80/1.htm">linux 基础</a>
                                    <div>               嵌入式系统设计中常用总线和接口 
  
        任何一个微处理器都要与一定数量的部件和外围设备连接,但如果将各部件和每一种外围设备都分别用一组线路与CPU直接连接,那么连线</div>
                                </li>
                                <li><a href="/article/1153.htm"
                                       title="Java函数调用方式——按值传递" target="_blank">Java函数调用方式——按值传递</a>
                                    <span class="text-muted">ayaoxinchao</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E6%8C%89%E5%80%BC%E4%BC%A0%E9%80%92/1.htm">按值传递</a><a class="tag" taget="_blank" href="/search/%E5%AF%B9%E8%B1%A1/1.htm">对象</a><a class="tag" taget="_blank" href="/search/%E5%9F%BA%E7%A1%80%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/1.htm">基础数据类型</a>
                                    <div>Java使用按值传递的函数调用方式,这往往使我感到迷惑。因为在基础数据类型和对象的传递上,我就会纠结于到底是按值传递,还是按引用传递。其实经过学习,Java在任何地方,都一直发挥着按值传递的本色。 
  
首先,让我们看一看基础数据类型是如何按值传递的。 
  
public static void main(String[] args) {
		
	int a = 2;
</div>
                                </li>
                                <li><a href="/article/1280.htm"
                                       title="ios音量线性下降" target="_blank">ios音量线性下降</a>
                                    <span class="text-muted">bewithme</span>
<a class="tag" taget="_blank" href="/search/ios%E9%9F%B3%E9%87%8F/1.htm">ios音量</a>
                                    <div>直接上代码吧 
  
//second 几秒内下降为0
- (void)reduceVolume:(int)second {
    KGVoicePlayer *player = [KGVoicePlayer defaultPlayer];
    
    if (!_flag) {
        _tempVolume = player.volume;
       </div>
                                </li>
                                <li><a href="/article/1407.htm"
                                       title="与其怨它不如爱它" target="_blank">与其怨它不如爱它</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/%E9%80%89%E6%8B%A9/1.htm">选择</a><a class="tag" taget="_blank" href="/search/%E7%90%86%E6%83%B3/1.htm">理想</a><a class="tag" taget="_blank" href="/search/%E8%81%8C%E4%B8%9A/1.htm">职业</a><a class="tag" taget="_blank" href="/search/%E8%A7%84%E5%88%92/1.htm">规划</a>
                                    <div>        抱怨工作是年轻人的常态,但爱工作才是积极的心态,与其怨它不如爱它。 
        一般来说,在公司干了一两年后,不少年轻人容易产生怨言,除了具体的埋怨公司“扭门”,埋怨上司无能以外,也有许多人是因为根本不爱自已的那份工作,工作完全成了谋生的手段,跟自已的性格、专业、爱好都相差甚远。 
  </div>
                                </li>
                                <li><a href="/article/1534.htm"
                                       title="一边时间不够用一边浪费时间" target="_blank">一边时间不够用一边浪费时间</a>
                                    <span class="text-muted">bingyingao</span>
<a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C/1.htm">工作</a><a class="tag" taget="_blank" href="/search/%E6%97%B6%E9%97%B4/1.htm">时间</a><a class="tag" taget="_blank" href="/search/%E6%B5%AA%E8%B4%B9/1.htm">浪费</a>
                                    <div>一方面感觉时间严重不够用,另一方面又在不停的浪费时间。 
 
每一个周末,晚上熬夜看电影到凌晨一点,早上起不来一直睡到10点钟,10点钟起床,吃饭后玩手机到下午一点。 
精神还是很差,下午像一直野鬼在城市里晃荡。 
 
 
为何不尝试晚上10点钟就睡,早上7点就起,时间完全是一样的,把看电影的时间换到早上,精神好,气色好,一天好状态。 
控制让自己周末早睡早起,你就成功了一半。 
 
有多少个工作</div>
                                </li>
                                <li><a href="/article/1661.htm"
                                       title="【Scala八】Scala核心二:隐式转换" target="_blank">【Scala八】Scala核心二:隐式转换</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/scala/1.htm">scala</a>
                                    <div>Implicits work like this: if you call a method on a Scala object, and the Scala compiler does not see a definition for that method in the class definition for that object, the compiler will try to con</div>
                                </li>
                                <li><a href="/article/1788.htm"
                                       title="sudoku slover in Haskell (2)" target="_blank">sudoku slover in Haskell (2)</a>
                                    <span class="text-muted">bookjovi</span>
<a class="tag" taget="_blank" href="/search/haskell/1.htm">haskell</a><a class="tag" taget="_blank" href="/search/sudoku/1.htm">sudoku</a>
                                    <div>继续精简haskell版的sudoku程序,稍微改了一下,这次用了8行,同时性能也提高了很多,对每个空格的所有解不是通过尝试算出来的,而是直接得出。 
  
board = [0,3,4,1,7,0,5,0,0,
         0,6,0,0,0,8,3,0,1,
         7,0,0,3,0,0,0,0,6,
         5,0,0,6,4,0,8,0,7,
</div>
                                </li>
                                <li><a href="/article/1915.htm"
                                       title="Java-Collections Framework学习与总结-HashSet和LinkedHashSet" target="_blank">Java-Collections Framework学习与总结-HashSet和LinkedHashSet</a>
                                    <span class="text-muted">BrokenDreams</span>
<a class="tag" taget="_blank" href="/search/linkedhashset/1.htm">linkedhashset</a>
                                    <div>        本篇总结一下两个常用的集合类HashSet和LinkedHashSet。 
        它们都实现了相同接口java.util.Set。Set表示一种元素无序且不可重复的集合;之前总结过的java.util.List表示一种元素可重复且有序</div>
                                </li>
                                <li><a href="/article/2042.htm"
                                       title="读《研磨设计模式》-代码笔记-备忘录模式-Memento" target="_blank">读《研磨设计模式》-代码笔记-备忘录模式-Memento</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/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a>
                                    <div>声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/ 
 
 

import java.util.ArrayList;
import java.util.List;



/*
 * 备忘录模式的功能是,在不破坏封装性的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态,为以后的状态恢复作“备忘”
</div>
                                </li>
                                <li><a href="/article/2169.htm"
                                       title="《RAW格式照片处理专业技法》笔记" target="_blank">《RAW格式照片处理专业技法》笔记</a>
                                    <span class="text-muted">cherishLC</span>
<a class="tag" taget="_blank" href="/search/PS/1.htm">PS</a>
                                    <div>注意,这不是教程!仅记录楼主之前不太了解的 
 
 一、色彩(空间)管理 
作者建议采用ProRGB(色域最广),但camera raw中设为ProRGB,而PS中则在ProRGB的基础上,将gamma值设为了1.8(更符合人眼) 
注意:bridge、camera raw怎么设置显示、输出的颜色都是正确的(会读取文件内的颜色配置文件),但用PS输出jpg文件时,必须先用Edit->conv</div>
                                </li>
                                <li><a href="/article/2296.htm"
                                       title="使用 Git 下载 Spring 源码 编译 for Eclipse" target="_blank">使用 Git 下载 Spring 源码 编译 for Eclipse</a>
                                    <span class="text-muted">crabdave</span>
<a class="tag" taget="_blank" href="/search/eclipse/1.htm">eclipse</a>
                                    <div>使用 Git 下载 Spring 源码 编译 for Eclipse 
  
1、安装gradle,下载 http://www.gradle.org/downloads 
配置环境变量GRADLE_HOME,配置PATH  %GRADLE_HOME%/bin,cmd,gradle -v 
  
2、spring4 用jdk8 下载 https://jdk8.java.</div>
                                </li>
                                <li><a href="/article/2423.htm"
                                       title="mysql连接拒绝问题" target="_blank">mysql连接拒绝问题</a>
                                    <span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/%E7%99%BB%E5%BD%95%E6%9D%83%E9%99%90/1.htm">登录权限</a>
                                    <div>mysql中在其它机器连接mysql服务器时报错问题汇总 
 
一、[running]root@192.168.9.136:~$mysql -uroot -h 192.168.9.108 -p   //带-p参数,在下一步进行密码输入 
Enter password:    //无字符串输入 
ERROR 1045 (28000): Access </div>
                                </li>
                                <li><a href="/article/2550.htm"
                                       title="Google Chrome 为何打压 H.264" target="_blank">Google Chrome 为何打压 H.264</a>
                                    <span class="text-muted">dsjt</span>
<a class="tag" taget="_blank" href="/search/apple/1.htm">apple</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/chrome/1.htm">chrome</a><a class="tag" taget="_blank" href="/search/Google/1.htm">Google</a>
                                    <div>Google 今天在 Chromium 官方博客宣布由于 H.264 编解码器并非开放标准,Chrome 将在几个月后正式停止对 H.264 视频解码的支持,全面采用开放的 WebM 和 Theora 格式。 
 
Google 在博客上表示,自从 WebM 视频编解码器推出以后,在性能、厂商支持以及独立性方面已经取得了很大的进步,为了与 Chromium 现有支持的編解码器保持一致,Chrome</div>
                                </li>
                                <li><a href="/article/2677.htm"
                                       title="yii 获取控制器名 和方法名" target="_blank">yii 获取控制器名 和方法名</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/yii/1.htm">yii</a><a class="tag" taget="_blank" href="/search/framework/1.htm">framework</a>
                                    <div>1. 获取控制器名 
在控制器中获取控制器名:  $name = $this->getId(); 
在视图中获取控制器名:    $name = Yii::app()->controller->id;  
  
2. 获取动作名  
在控制器beforeAction()回调函数中获取动作名:  $name = </div>
                                </li>
                                <li><a href="/article/2804.htm"
                                       title="Android知识总结(二)" target="_blank">Android知识总结(二)</a>
                                    <span class="text-muted">come_for_dream</span>
<a class="tag" taget="_blank" href="/search/android/1.htm">android</a>
                                    <div>明天要考试了,速速总结如下 
  
1、Activity的启动模式 
       standard:每次调用Activity的时候都创建一个(可以有多个相同的实例,也允许多个相同Activity叠加。) 
       singleTop:可以有多个实例,但是不允许多个相同Activity叠加。即,如果Ac</div>
                                </li>
                                <li><a href="/article/2931.htm"
                                       title="高洛峰收徒第二期:寻找未来的“技术大牛” ——折腾一年,奖励20万元" target="_blank">高洛峰收徒第二期:寻找未来的“技术大牛” ——折腾一年,奖励20万元</a>
                                    <span class="text-muted">gcq511120594</span>
<a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C/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>
                                    <div>高洛峰,兄弟连IT教育合伙人、猿代码创始人、PHP培训第一人、《细说PHP》作者、软件开发工程师、《IT峰播》主创人、PHP讲师的鼻祖! 
首期现在的进程刚刚过半,徒弟们真的很棒,人品都没的说,团结互助,学习刻苦,工作认真积极,灵活上进。我几乎会把他们全部留下来,现在已有一多半安排了实际的工作,并取得了很好的成绩。等他们出徒之日,凭他们的能力一定能够拿到高薪,而且我还承诺过一个徒弟,当他拿到大学毕</div>
                                </li>
                                <li><a href="/article/3058.htm"
                                       title="linux expect" target="_blank">linux expect</a>
                                    <span class="text-muted">heipark</span>
<a class="tag" taget="_blank" href="/search/expect/1.htm">expect</a>
                                    <div>1. 创建、编辑文件go.sh 
  
#!/usr/bin/expect

spawn sudo su admin

expect  "*password*" { send "13456\r\n" }

interact  
   2. 设置权限 
   chmod u+x go.sh   3.</div>
                                </li>
                                <li><a href="/article/3185.htm"
                                       title="Spring4.1新特性——静态资源处理增强" target="_blank">Spring4.1新特性——静态资源处理增强</a>
                                    <span class="text-muted">jinnianshilongnian</span>
<a class="tag" taget="_blank" href="/search/spring+4.1/1.htm">spring 4.1</a>
                                    <div>目录 
Spring4.1新特性——综述 
Spring4.1新特性——Spring核心部分及其他 
Spring4.1新特性——Spring缓存框架增强 
Spring4.1新特性——异步调用和事件机制的异常处理 
Spring4.1新特性——数据库集成测试脚本初始化 
Spring4.1新特性——Spring MVC增强 
Spring4.1新特性——页面自动化测试框架Spring MVC T</div>
                                </li>
                                <li><a href="/article/3312.htm"
                                       title="idea ubuntuxia 乱码" target="_blank">idea ubuntuxia 乱码</a>
                                    <span class="text-muted">liyonghui160com</span>

                                    <div>    
1.首先需要在windows字体目录下或者其它地方找到simsun.ttf 这个 字体文件。 
2.在ubuntu 下可以执行下面操作安装该字体: 
sudo mkdir /usr/share/fonts/truetype/simsun
sudo cp simsun.ttf  /usr/share/fonts/truetype/simsun
fc-cache -f -v 
</div>
                                </li>
                                <li><a href="/article/3439.htm"
                                       title="改良程序的11技巧" target="_blank">改良程序的11技巧</a>
                                    <span class="text-muted">pda158</span>
<a class="tag" taget="_blank" href="/search/%E6%8A%80%E5%B7%A7/1.htm">技巧</a>
                                    <div>有很多理由都能说明为什么我们应该写出清晰、可读性好的程序。最重要的一点,程序你只写一次,但以后会无数次的阅读。当你第二天回头来看你的代码 时,你就要开始阅读它了。当你把代码拿给其他人看时,他必须阅读你的代码。因此,在编写时多花一点时间,你会在阅读它时节省大量的时间。 
  
让我们看一些基本的编程技巧: 
  
 
 尽量保持方法简短 
 永远永远不要把同一个变量用于多个不同的</div>
                                </li>
                                <li><a href="/article/3566.htm"
                                       title="300个涵盖IT各方面的免费资源(下)——工作与学习篇" target="_blank">300个涵盖IT各方面的免费资源(下)——工作与学习篇</a>
                                    <span class="text-muted">shoothao</span>
<a class="tag" taget="_blank" href="/search/%E5%88%9B%E4%B8%9A/1.htm">创业</a><a class="tag" taget="_blank" href="/search/%E5%85%8D%E8%B4%B9%E8%B5%84%E6%BA%90/1.htm">免费资源</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%AF%BE%E7%A8%8B/1.htm">学习课程</a><a class="tag" taget="_blank" href="/search/%E8%BF%9C%E7%A8%8B%E5%B7%A5%E4%BD%9C/1.htm">远程工作</a>
                                    <div>工作与生产效率: 
  
 
 A. 背景声音 
 
 
  Noisli:背景噪音与颜色生成器。 
  Noizio:环境声均衡器。 
  Defonic:世界上任何的声响都可混合成美丽的旋律。 
  Designers.mx:设计者为设计者所准备的播放列表。 
  Coffitivity:这里的声音就像咖啡馆里放的一样。 
 
 
 B. 避免注意力分散 
 
 
  Self Co</div>
                                </li>
                                <li><a href="/article/3693.htm"
                                       title="深入浅出RPC" target="_blank">深入浅出RPC</a>
                                    <span class="text-muted">uule</span>
<a class="tag" taget="_blank" href="/search/rpc/1.htm">rpc</a>
                                    <div>深入浅出RPC-浅出篇 
深入浅出RPC-深入篇 
  
RPC 
Remote Procedure Call Protocol 
远程过程调用协议 
  
它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发</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>