async 函数(转载)

async 函数

含义

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

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

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

const fs = require('fs');

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

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

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

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

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

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

(1)内置执行器。

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

asyncReadFile();

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

(2)更好的语义。

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

(3)更广的适用性。

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

(4)返回值是 Promise。

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

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

基本用法

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

下面是一个例子。

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

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

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

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

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

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

asyncPrint('hello world', 50);

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

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

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

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

asyncPrint('hello world', 50);

async 函数有多种使用形式。

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

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

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

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

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

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

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

语法

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

返回 Promise 对象

async函数返回一个 Promise 对象。

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

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

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

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

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

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

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

Promise 对象的状态变化

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

下面是一个例子。

async function getTitle(url) {
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/([\s\S]+)<\/title>/i)[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 对象。如果不是,会被转成一个立即<code>resolve</code>的 Promise 对象。</p> 
 <pre><code class="javascript">async function f() {
  return await 123;
}

f().then(v => console.log(v))
// 123
</code></pre> 
 <p>上面代码中,<code>await</code>命令的参数是数值<code>123</code>,它被转成 Promise 对象,并立即<code>resolve</code>。</p> 
 <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>目前,<code>@std/esm</code>模块加载器支持顶层<code>await</code>,即<code>await</code>命令可以不放在 async 函数里面,直接使用。</p> 
 <pre><code class="javascript">// async 函数的写法
const start = async () => {
  const res = await fetch('google.com');
  return res.text();
};

start().then(console.log);

// 顶层 await 的写法
const res = await fetch('google.com');
console.log(await res.text());
</code></pre> 
 <p>上面代码中,第二种写法的脚本必须使用<code>@std/esm</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>异步遍历器</h2> 
 <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>目前,有一个提案,为异步操作提供原生的遍历器接口,即<code>value</code>和<code>done</code>这两个属性都是异步产生,这称为”异步遍历器“(Async Iterator)。</p> 
 <h3>异步遍历的接口</h3> 
 <p>异步遍历器的最大的语法特点,就是调用遍历器的<code>next</code>方法,返回的是一个 Promise 对象。</p> 
 <pre><code class="javascript">asyncIterator
  .next()
  .then(
    ({ value, done }) => /* ... */
  );
</code></pre> 
 <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> 
 <pre><code class="javascript">const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();

asyncIterator
.next()
.then(iterResult1 => {
  console.log(iterResult1); // { value: 'a', done: false }
  return asyncIterator.next();
})
.then(iterResult2 => {
  console.log(iterResult2); // { value: 'b', done: false }
  return asyncIterator.next();
})
.then(iterResult3 => {
  console.log(iterResult3); // { value: undefined, done: true }
});
</code></pre> 
 <p>上面代码中,异步遍历器其实返回了两次值。第一次调用的时候,返回一个 Promise 对象;等到 Promise 对象<code>resolve</code>了,再返回一个表示当前数据成员信息的对象。这就是说,异步遍历器与同步遍历器最终行为是一致的,只是会先返回 Promise 对象,作为中介。</p> 
 <p>由于异步遍历器的<code>next</code>方法,返回的是一个 Promise 对象。因此,可以把它放在<code>await</code>命令后面。</p> 
 <pre><code class="javascript">async function f() {
  const asyncIterable = createAsyncIterable(['a', 'b']);
  const asyncIterator = asyncIterable[Symbol.asyncIterator]();
  console.log(await asyncIterator.next());
  // { value: 'a', done: false }
  console.log(await asyncIterator.next());
  // { value: 'b', done: false }
  console.log(await asyncIterator.next());
  // { value: undefined, done: true }
}
</code></pre> 
 <p>上面代码中,<code>next</code>方法用<code>await</code>处理以后,就不必使用<code>then</code>方法了。整个流程已经很接近同步处理了。</p> 
 <p>注意,异步遍历器的<code>next</code>方法是可以连续调用的,不必等到上一步产生的Promise对象<code>resolve</code>以后再调用。这种情况下,<code>next</code>方法会累积起来,自动按照每一步的顺序运行下去。下面是一个例子,把所有的<code>next</code>方法放在<code>Promise.all</code>方法里面。</p> 
 <pre><code class="javascript">const asyncGenObj = createAsyncIterable(['a', 'b']);
const [{value: v1}, {value: v2}] = await Promise.all([
  asyncGenObj.next(), asyncGenObj.next()
]);

console.log(v1, v2); // a b
</code></pre> 
 <p>另一种用法是一次性调用所有的<code>next</code>方法,然后<code>await</code>最后一步操作。</p> 
 <pre><code class="javascript">const writer = openFile('someFile.txt');
writer.next('hello');
writer.next('world');
await writer.return();
</code></pre> 
 <h3>for await...of</h3> 
 <p>前面介绍过,<code>for...of</code>循环用于遍历同步的 Iterator 接口。新引入的<code>for await...of</code>循环,则是用于遍历异步的 Iterator 接口。</p> 
 <pre><code class="javascript">async function f() {
  for await (const x of createAsyncIterable(['a', 'b'])) {
    console.log(x);
  }
}
// a
// b
</code></pre> 
 <p>上面代码中,<code>createAsyncIterable()</code>返回一个异步遍历器,<code>for...of</code>循环自动调用这个遍历器的<code>next</code>方法,会得到一个Promise对象。<code>await</code>用来处理这个Promise对象,一旦<code>resolve</code>,就把得到的值(<code>x</code>)传入<code>for...of</code>的循环体。</p> 
 <p><code>for await...of</code>循环的一个用途,是部署了 asyncIterable 操作的异步接口,可以直接放入这个循环。</p> 
 <pre><code class="javascript">let body = '';

async function f() {
  for await(const data of req) body += data;
  const parsed = JSON.parse(body);
  console.log('got', parsed);
}
</code></pre> 
 <p>上面代码中,<code>req</code>是一个 asyncIterable 对象,用来异步读取数据。可以看到,使用<code>for await...of</code>循环以后,代码会非常简洁。</p> 
 <p>如果<code>next</code>方法返回的 Promise 对象被<code>reject</code>,<code>for await...of</code>就会报错,要用<code>try...catch</code>捕捉。</p> 
 <pre><code class="javascript">async function () {
  try {
    for await (const x of createRejectingIterable()) {
      console.log(x);
    }
  } catch (e) {
    console.error(e);
  }
}
</code></pre> 
 <p>注意,<code>for await...of</code>循环也可以用于同步遍历器。</p> 
 <pre><code class="javascript">(async function () {
  for await (const x of ['a', 'b']) {
    console.log(x);
  }
})();
// a
// b
</code></pre> 
 <h3>异步 Generator 函数</h3> 
 <p>就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。</p> 
 <p>在语法上,异步 Generator 函数就是<code>async</code>函数与 Generator 函数的结合。</p> 
 <pre><code class="javascript">async function* gen() {
  yield 'hello';
}
const genObj = gen();
genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }
</code></pre> 
 <p>上面代码中,<code>gen</code>是一个异步 Generator 函数,执行后返回一个异步 Iterator 对象。对该对象调用<code>next</code>方法,返回一个 Promise 对象。</p> 
 <p>异步遍历器的设计目的之一,就是 Generator 函数处理同步操作和异步操作时,能够使用同一套接口。</p> 
 <pre><code class="javascript">// 同步 Generator 函数
function* map(iterable, func) {
  const iter = iterable[Symbol.iterator]();
  while (true) {
    const {value, done} = iter.next();
    if (done) break;
    yield func(value);
  }
}

// 异步 Generator 函数
async function* map(iterable, func) {
  const iter = iterable[Symbol.asyncIterator]();
  while (true) {
    const {value, done} = await iter.next();
    if (done) break;
    yield func(value);
  }
}
</code></pre> 
 <p>上面代码中,可以看到有了异步遍历器以后,同步 Generator 函数和异步 Generator 函数的写法基本上是一致的。</p> 
 <p>下面是另一个异步 Generator 函数的例子。</p> 
 <pre><code class="javascript">async function* readLines(path) {
  let file = await fileOpen(path);

  try {
    while (!file.EOF) {
      yield await file.readLine();
    }
  } finally {
    await file.close();
  }
}
</code></pre> 
 <p>上面代码中,异步操作前面使用<code>await</code>关键字标明,即<code>await</code>后面的操作,应该返回 Promise 对象。凡是使用<code>yield</code>关键字的地方,就是<code>next</code>方法的停下来的地方,它后面的表达式的值(即<code>await file.readLine()</code>的值),会作为<code>next()</code>返回对象的<code>value</code>属性,这一点是与同步 Generator 函数一致的。</p> 
 <p>异步 Generator 函数内部,能够同时使用<code>await</code>和<code>yield</code>命令。可以这样理解,<code>await</code>命令用于将外部操作产生的值输入函数内部,<code>yield</code>命令用于将函数内部的值输出。</p> 
 <p>上面代码定义的异步 Generator 函数的用法如下。</p> 
 <pre><code class="javascript">(async function () {
  for await (const line of readLines(filePath)) {
    console.log(line);
  }
})()
</code></pre> 
 <p>异步 Generator 函数可以与<code>for await...of</code>循环结合起来使用。</p> 
 <pre><code class="javascript">async function* prefixLines(asyncIterable) {
  for await (const line of asyncIterable) {
    yield '> ' + line;
  }
}
</code></pre> 
 <p>异步 Generator 函数的返回值是一个异步 Iterator,即每次调用它的<code>next</code>方法,会返回一个 Promise 对象,也就是说,跟在<code>yield</code>命令后面的,应该是一个 Promise 对象。</p> 
 <pre><code class="javascript">async function* asyncGenerator() {
  console.log('Start');
  const result = await doSomethingAsync(); // (A)
  yield 'Result: '+ result; // (B)
  console.log('Done');
}

const ag = asyncGenerator();
ag.next().then({value, done} => {
  // ...
})
</code></pre> 
 <p>上面代码中,<code>ag</code>是<code>asyncGenerator</code>函数返回的异步 Iterator 对象。调用<code>ag.next()</code>以后,<code>asyncGenerator</code>函数内部的执行顺序如下。</p> 
 <ol> 
  <li>打印出<code>Start</code>。</li> 
  <li> <code>await</code>命令返回一个 Promise 对象,但是程序不会停在这里,继续往下执行。</li> 
  <li>程序在<code>B</code>处暂停执行,<code>yield</code>命令立刻返回一个 Promise 对象,该对象就是<code>ag.next()</code>的返回值。</li> 
  <li> <code>A</code>处<code>await</code>命令后面的那个 Promise 对象 resolved,产生的值放入<code>result</code>变量。</li> 
  <li> <code>B</code>处的 Promise 对象 resolved,<code>then</code>方法指定的回调函数开始执行,该函数的参数是一个对象,<code>value</code>的值是表达式<code>'Result: ' + result</code>的值,<code>done</code>属性的值是<code>false</code>。</li> 
 </ol> 
 <p>A 和 B 两行的作用类似于下面的代码。</p> 
 <pre><code class="javascript">return new Promise((resolve, reject) => {
  doSomethingAsync()
  .then(result => {
     resolve({
       value: 'Result: '+result,
       done: false,
     });
  });
});
</code></pre> 
 <p>如果异步 Generator 函数抛出错误,会被 Promise 对象<code>reject</code>,然后抛出的错误被<code>catch</code>方法捕获。</p> 
 <pre><code class="javascript">async function* asyncGenerator() {
  throw new Error('Problem!');
}

asyncGenerator()
.next()
.catch(err => console.log(err)); // Error: Problem!
</code></pre> 
 <p>注意,普通的 async 函数返回的是一个 Promise 对象,而异步 Generator 函数返回的是一个异步 Iterator 对象。可以这样理解,async 函数和异步 Generator 函数,是封装异步操作的两种方法,都用来达到同一种目的。区别在于,前者自带执行器,后者通过<code>for await...of</code>执行,或者自己编写执行器。下面就是一个异步 Generator 函数的执行器。</p> 
 <pre><code class="javascript">async function takeAsync(asyncIterable, count = Infinity) {
  const result = [];
  const iterator = asyncIterable[Symbol.asyncIterator]();
  while (result.length < count) {
    const {value, done} = await iterator.next();
    if (done) break;
    result.push(value);
  }
  return result;
}
</code></pre> 
 <p>上面代码中,异步 Generator 函数产生的异步遍历器,会通过<code>while</code>循环自动执行,每当<code>await iterator.next()</code>完成,就会进入下一轮循环。一旦<code>done</code>属性变为<code>true</code>,就会跳出循环,异步遍历器执行结束。</p> 
 <p>下面是这个自动执行器的一个使用实例。</p> 
 <pre><code class="javascript">async function f() {
  async function* gen() {
    yield 'a';
    yield 'b';
    yield 'c';
  }

  return await takeAsync(gen());
}

f().then(function (result) {
  console.log(result); // ['a', 'b', 'c']
})
</code></pre> 
 <p>异步 Generator 函数出现以后,JavaScript 就有了四种函数形式:普通函数、async 函数、Generator 函数和异步 Generator 函数。请注意区分每种函数的不同之处。基本上,如果是一系列按照顺序执行的异步操作(比如读取文件,然后写入新内容,再存入硬盘),可以使用 async 函数;如果是一系列产生相同数据结构的异步操作(比如一行一行读取文件),可以使用异步 Generator 函数。</p> 
 <p>异步 Generator 函数也可以通过<code>next</code>方法的参数,接收外部传入的数据。</p> 
 <pre><code class="javascript">const writer = openFile('someFile.txt');
writer.next('hello'); // 立即执行
writer.next('world'); // 立即执行
await writer.return(); // 等待写入结束
</code></pre> 
 <p>上面代码中,<code>openFile</code>是一个异步 Generator 函数。<code>next</code>方法的参数,向该函数内部的操作传入数据。每次<code>next</code>方法都是同步执行的,最后的<code>await</code>命令用于等待整个写入操作结束。</p> 
 <p>最后,同步的数据结构,也可以使用异步 Generator 函数。</p> 
 <pre><code class="javascript">async function* createAsyncIterable(syncIterable) {
  for (const elem of syncIterable) {
    yield elem;
  }
}
</code></pre> 
 <p>上面代码中,由于没有异步操作,所以也就没有使用<code>await</code>关键字。</p> 
 <h3>yield* 语句</h3> 
 <p><code>yield*</code>语句也可以跟一个异步遍历器。</p> 
 <pre><code class="javascript">async function* gen1() {
  yield 'a';
  yield 'b';
  return 2;
}

async function* gen2() {
  // result 最终会等于 2
  const result = yield* gen1();
}
</code></pre> 
 <p>上面代码中,<code>gen2</code>函数里面的<code>result</code>变量,最后的值是<code>2</code>。</p> 
 <p>与同步 Generator 函数一样,<code>for await...of</code>循环会展开<code>yield*</code>。</p> 
 <pre><code class="javascript">(async function () {
  for await (const x of gen2()) {
    console.log(x);
  }
})();
// a
// b
</code></pre> 
</article>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1214421292237819904"></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">你可能感兴趣的:(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/1950199532725792768.htm"
                           title="Android GreenDao介绍和Generator生成表对象代码" target="_blank">Android GreenDao介绍和Generator生成表对象代码</a>
                        <span class="text-muted"></span>

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

                        <div>+、-、*、/、++、--、==、!=、*->、&&、||...对于内置数据类型,编译器知道如何做运算,编译器不知道如何让两个类进行运算如果向让自定义数据类型进行+法运算,就需要重载+运算符在成员函数或者全局函数里,重写一个+法运算符的函数函数名operator+(){}运算符重载也可以提供多个版本加法运算符类名+operator+(){};例:成员函数classPerson{public:Per</div>
                    </li>
                    <li><a href="/article/1950161706533580800.htm"
                           title="SQL笔记纯干货" target="_blank">SQL笔记纯干货</a>
                        <span class="text-muted">AI入门修炼</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a>
                        <div>软件:DataGrip2023.2.3,phpstudy_pro,MySQL8.0.12目录1.DDL语句(数据定义语句)1.1数据库操作语言1.2数据表操作语言2.DML语句(数据操作语言)2.1增删改2.2题2.3备份表3.DQL语句(数据查询语言)3.1查询操作3.2题一3.3题二4.多表详解4.1一对多4.2多对多5.多表查询6.窗口函数7.拓展:upsert8.sql注入攻击演示9.拆表</div>
                    </li>
                    <li><a href="/article/1950158303287898112.htm"
                           title="零数学基础理解AI核心概念:梯度下降可视化实战" target="_blank">零数学基础理解AI核心概念:梯度下降可视化实战</a>
                        <span class="text-muted">九章云极AladdinEdu</span>
<a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/gpu%E7%AE%97%E5%8A%9B/1.htm">gpu算力</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/pytorch/1.htm">pytorch</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/1.htm">语言模型</a><a class="tag" taget="_blank" href="/search/opencv/1.htm">opencv</a>
                        <div>点击“AladdinEdu,同学们用得起的【H卡】算力平台”,H卡级别算力,按量计费,灵活弹性,顶级配置,学生专属优惠。用Python动画演示损失函数优化过程,数学公式具象化读者收获:直观理解模型训练本质,破除"数学恐惧症"当盲人登山者摸索下山路径时,他本能地运用了梯度下降算法。本文将用动态可视化技术,让你像感受重力一样理解AI训练的核心原理——无需任何数学公式推导。一、梯度下降:AI世界的"万有</div>
                    </li>
                    <li><a href="/article/1950146206252462080.htm"
                           title="OnJava8-学习分享(附资源)" target="_blank">OnJava8-学习分享(附资源)</a>
                        <span class="text-muted">李超同学</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E4%B9%A6%E7%B1%8D/1.htm">书籍</a><a class="tag" taget="_blank" href="/search/onjava8/1.htm">onjava8</a>
                        <div>本书是布鲁斯•埃克尔时隔15年,继ThinkinginJava之后又一力作,基于Java的3个长期支持版(Java8、11、17),讲解Java核心语法,并对Java的核心变化进行详述。全书内容通俗易懂,配合示例讲解逐步深入,并结合实际开发需要,从语言底层设计出发,有效帮读者规避一些常见的开发陷阱。主体部分共22章,内容包含对象、操作符、控制流、初始化和清理、复用、多态、接口、内部类、集合、函数式</div>
                    </li>
                    <li><a href="/article/1950138760230400000.htm"
                           title="C语言基础-数组和指针的区别" target="_blank">C语言基础-数组和指针的区别</a>
                        <span class="text-muted">阿部春光</span>
<a class="tag" taget="_blank" href="/search/C%E8%AF%AD%E8%A8%80/1.htm">C语言</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>在C语言中,数组和指针是两个密切相关但又有显著区别的概念。下面我会详细解释它们之间的区别和联系。区别数组和指针在C语言中虽然经常一起使用,但它们是两个不同的概念,具有一些关键的区别:本质不同:数组:数组是一种数据结构,用于存储固定数量的同类型元素的连续内存块。数组名在某些上下文中(如取地址操作或sizeof操作符)代表整个数组,但在其他上下文中(如作为函数参数或用于指针算术)通常退化为指向数组第一</div>
                    </li>
                    <li><a href="/article/1950138508161118208.htm"
                           title="如何学好图像处理——从小白到大神?" target="_blank">如何学好图像处理——从小白到大神?</a>
                        <span class="text-muted">chentengkui</span>
<a class="tag" taget="_blank" href="/search/%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86/1.htm">图像处理</a>
                        <div>如何学好图像处理——从小白到大神?标签:图像处理学习方法2016-02-2617:4818439人阅读评论(25)收藏举报分类:学习方法与方法论(13)版权声明:本文为博主原创文章,未经博主允许不得转载。什么是数字图像处理?历史、以及它所研究的内容。说起图像处理,你会想到什么?你是否真的了解这个领域所研究的内容。纵向来说,数字图像处理研究的历史相当悠久;横向来说,数字图像处理研究的话题相当广泛。数</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/1950135988265218048.htm"
                           title="高铁站违规撑伞识别误检率↓79%:陌讯多模态融合算法实战解析" target="_blank">高铁站违规撑伞识别误检率↓79%:陌讯多模态融合算法实战解析</a>
                        <span class="text-muted">2501_92722744</span>
<a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/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/%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8B/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/%E7%9B%AE%E6%A0%87%E8%B7%9F%E8%B8%AA/1.htm">目标跟踪</a>
                        <div>原创声明本文为原创技术解析,核心技术参数与架构参考自《陌讯技术白皮书》,禁止未经授权的转载与改编。一、行业痛点:密集场景下的违规撑伞识别难题在高铁站、地铁站等交通枢纽,违规撑伞(如非雨天在站台、通道内持伞)可能引发客流拥堵、设备刮擦等安全隐患。然而,传统视觉识别方案面临三大核心挑战:环境干扰大:进出站口光线突变(正午强光/夜间弱光)导致伞面特征提取不稳定,某枢纽站点实测数据显示,阴雨天违规撑伞识别</div>
                    </li>
                    <li><a href="/article/1950135987115978752.htm"
                           title="建筑施工场景下漏检率↓76%!陌讯多模态融合算法在工程安全监控的落地实践" target="_blank">建筑施工场景下漏检率↓76%!陌讯多模态融合算法在工程安全监控的落地实践</a>
                        <span class="text-muted">2501_92722744</span>
<a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/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%9B%AE%E6%A0%87%E8%B7%9F%E8%B8%AA/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%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/1.htm">计算机视觉</a><a class="tag" taget="_blank" href="/search/%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8B/1.htm">目标检测</a>
                        <div>原创声明本文为原创技术解析,核心技术参数与架构设计引用自《陌讯技术白皮书》,转载请注明来源。一、行业痛点:建筑施工监控的技术瓶颈建筑施工场景的安全监控长期面临多重技术挑战:数据支撑:据《2023建筑施工安全自动化监控报告》显示,传统监控系统对“未佩戴安全帽”“高空抛物”等危险行为的漏检率超35%,误报率高达42%,导致安全事故响应滞后[7]。场景难点:工地存在强光直射(正午阳光)、动态遮挡(塔吊/</div>
                    </li>
                    <li><a href="/article/1950132762543452160.htm"
                           title="想踩着我往上爬的闺蜜,栽了!" target="_blank">想踩着我往上爬的闺蜜,栽了!</a>
                        <span class="text-muted">木木爱电影</span>

                        <div>来源微信公众号:芒小芒,如需转载请联系该公众号,谢谢。011999年7月,一出考场,我就感觉自己考砸了。收拾完行李,家都没回,我直接投奔在城里做服装生意的表哥。表哥早些年在南方工厂打工,攒下资本,从小摊起,打拼七八年,终于在这座省会城市市中心商厦里占据一席之地。表哥店里只卖女装,二十来平米的空间,摆着一件件设计新颖、做工精良的时装——表哥说,世界上最好挣的就是孩子和女人的钱。服装这个行业,不干不知</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/73.htm"
                                       title="Hadoop(一)" target="_blank">Hadoop(一)</a>
                                    <span class="text-muted">朱辉辉33</span>
<a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a>
                                    <div>今天在诺基亚第一天开始培训大数据,因为之前没接触过Linux,所以这次一起学了,任务量还是蛮大的。 
首先下载安装了Xshell软件,然后公司给了账号密码连接上了河南郑州那边的服务器,接下来开始按照给的资料学习,全英文的,头也不讲解,说锻炼我们的学习能力,然后就开始跌跌撞撞的自学。这里写部分已经运行成功的代码吧. 
   在hdfs下,运行hadoop fs -mkdir /u</div>
                                </li>
                                <li><a href="/article/200.htm"
                                       title="maven An error occurred while filtering resources" target="_blank">maven An error occurred while filtering resources</a>
                                    <span class="text-muted">blackproof</span>
<a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a><a class="tag" taget="_blank" href="/search/%E6%8A%A5%E9%94%99/1.htm">报错</a>
                                    <div>转:http://stackoverflow.com/questions/18145774/eclipse-an-error-occurred-while-filtering-resources 
  
maven报错: 
maven An error occurred while filtering resources 
  
Maven -> Update Proje</div>
                                </li>
                                <li><a href="/article/327.htm"
                                       title="jdk常用故障排查命令" target="_blank">jdk常用故障排查命令</a>
                                    <span class="text-muted">daysinsun</span>
<a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a>
                                    <div>linux下常见定位命令: 
1、jps      输出Java进程 
      -q       只输出进程ID的名称,省略主类的名称; 
      -m      输出进程启动时传递给main函数的参数; 
    &nb</div>
                                </li>
                                <li><a href="/article/454.htm"
                                       title="java 位移运算与乘法运算" target="_blank">java 位移运算与乘法运算</a>
                                    <span class="text-muted">周凡杨</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E4%BD%8D%E7%A7%BB/1.htm">位移</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%AE%97/1.htm">运算</a><a class="tag" taget="_blank" href="/search/%E4%B9%98%E6%B3%95/1.htm">乘法</a>
                                    <div>  
对于 JAVA 编程中,适当的采用位移运算,会减少代码的运行时间,提高项目的运行效率。这个可以从一道面试题说起:  
   
   问题:  
用最有效率的方法算出2   乘以8   等於几?”        
答案:2 << 3    
由此就引发了我的思考,为什么位移运算会比乘法运算更快呢?其实简单的想想,计算机的内存是用由 0 和 1 组成的二</div>
                                </li>
                                <li><a href="/article/581.htm"
                                       title="java中的枚举(enmu)" target="_blank">java中的枚举(enmu)</a>
                                    <span class="text-muted">g21121</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>从jdk1.5开始,java增加了enum(枚举)这个类型,但是大家在平时运用中还是比较少用到枚举的,而且很多人和我一样对枚举一知半解,下面就跟大家一起学习下enmu枚举。先看一个最简单的枚举类型,一个返回类型的枚举: 
public enum ResultType {
	
	/**
	 * 成功
	 */
	SUCCESS,
	/**
	 * 失败
	 */
	FAIL,
	</div>
                                </li>
                                <li><a href="/article/708.htm"
                                       title="MQ初级学习" target="_blank">MQ初级学习</a>
                                    <span class="text-muted">510888780</span>
<a class="tag" taget="_blank" href="/search/activemq/1.htm">activemq</a>
                                    <div>1.下载ActiveMQ 
去官方网站下载:http://activemq.apache.org/ 
 
2.运行ActiveMQ 
解压缩apache-activemq-5.9.0-bin.zip到C盘,然后双击apache-activemq-5.9.0-\bin\activemq-admin.bat运行ActiveMQ程序。 
 
启动ActiveMQ以后,登陆:http://localhos</div>
                                </li>
                                <li><a href="/article/835.htm"
                                       title="Spring_Transactional_Propagation" target="_blank">Spring_Transactional_Propagation</a>
                                    <span class="text-muted">布衣凌宇</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/transactional/1.htm">transactional</a>
                                    <div>//事务传播属性 
@Transactional(propagation=Propagation.REQUIRED)//如果有事务,那么加入事务,没有的话新创建一个 
@Transactional(propagation=Propagation.NOT_SUPPORTED)//这个方法不开启事务 
@Transactional(propagation=Propagation.REQUIREDS_N</div>
                                </li>
                                <li><a href="/article/962.htm"
                                       title="我的spring学习笔记12-idref与ref的区别" target="_blank">我的spring学习笔记12-idref与ref的区别</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                                    <div>idref用来将容器内其他bean的id传给<constructor-arg>/<property>元素,同时提供错误验证功能。例如: 
 
<bean id ="theTargetBean" class="..." />
<bean id ="theClientBean" class=&quo</div>
                                </li>
                                <li><a href="/article/1089.htm"
                                       title="Jqplot之折线图" target="_blank">Jqplot之折线图</a>
                                    <span class="text-muted">antlove</span>
<a class="tag" taget="_blank" href="/search/js/1.htm">js</a><a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/timeseries/1.htm">timeseries</a><a class="tag" taget="_blank" href="/search/jqplot/1.htm">jqplot</a>
                                    <div>timeseriesChart.html 
<script type="text/javascript" src="jslib/jquery.min.js"></script>  
<script type="text/javascript" src="jslib/excanvas.min.js&</div>
                                </li>
                                <li><a href="/article/1216.htm"
                                       title="JDBC中事务处理应用" target="_blank">JDBC中事务处理应用</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/JDBC%E7%BC%96%E7%A8%8B/1.htm">JDBC编程</a><a class="tag" taget="_blank" href="/search/%E4%BA%8B%E5%8A%A1%E6%8E%A7%E5%88%B6%E8%AF%AD%E5%8F%A5/1.htm">事务控制语句</a>
                                    <div>  
解释事务的概念; 事务控制是sql语句中的核心之一;事务控制的作用就是保证数据的正常执行与异常之后可以恢复 
  
事务常用命令: 
            Commit提交 
        </div>
                                </li>
                                <li><a href="/article/1343.htm"
                                       title="[转]ConcurrentHashMap Collections.synchronizedMap和Hashtable讨论" target="_blank">[转]ConcurrentHashMap Collections.synchronizedMap和Hashtable讨论</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">多线程</a><a class="tag" taget="_blank" href="/search/%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8/1.htm">线程安全</a><a class="tag" taget="_blank" href="/search/HashMap/1.htm">HashMap</a>
                                    <div>在Java类库中出现的第一个关联的集合类是Hashtable,它是JDK1.0的一部分。 Hashtable提供了一种易于使用的、线程安全的、关联的map功能,这当然也是方便的。然而,线程安全性是凭代价换来的――Hashtable的所有方法都是同步的。此时,无竞争的同步会导致可观的性能代价。Hashtable的后继者HashMap是作为JDK1.2中的集合框架的一部分出现的,它通过提供一个不同步的</div>
                                </li>
                                <li><a href="/article/1470.htm"
                                       title="ng-if与ng-show、ng-hide指令的区别和注意事项" target="_blank">ng-if与ng-show、ng-hide指令的区别和注意事项</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/AngularJS/1.htm">AngularJS</a>
                                    <div>        angularJS中的ng-show、ng-hide、ng-if指令都可以用来控制dom元素的显示或隐藏。ng-show和ng-hide根据所给表达式的值来显示或隐藏HTML元素。当赋值给ng-show指令的值为false时元素会被隐藏,值为true时元素会显示。ng-hide功能类似,使用方式相反。元素的显示或</div>
                                </li>
                                <li><a href="/article/1597.htm"
                                       title="【持久化框架MyBatis3七】MyBatis3定义typeHandler" target="_blank">【持久化框架MyBatis3七】MyBatis3定义typeHandler</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/TypeHandler/1.htm">TypeHandler</a>
                                    <div>什么是typeHandler? 
typeHandler用于将某个类型的数据映射到表的某一列上,以完成MyBatis列跟某个属性的映射 
  内置typeHandler 
MyBatis内置了很多typeHandler,这写typeHandler通过org.apache.ibatis.type.TypeHandlerRegistry进行注册,比如对于日期型数据的typeHandler, </div>
                                </li>
                                <li><a href="/article/1724.htm"
                                       title="上传下载文件rz,sz命令" target="_blank">上传下载文件rz,sz命令</a>
                                    <span class="text-muted">bitcarter</span>
<a class="tag" taget="_blank" href="/search/linux%E5%91%BD%E4%BB%A4rz/1.htm">linux命令rz</a>
                                    <div>刚开始使用rz上传和sz下载命令: 
因为我们是通过secureCRT终端工具进行使用的所以会有上传下载这样的需求: 
 
我遇到的问题: 
sz下载A文件10M左右,没有问题 
但是将这个文件A再传到另一天服务器上时就出现传不上去,甚至出现乱码,死掉现象,具体问题 
 
解决方法: 
上传命令改为;rz -ybe 
下载命令改为:sz -be filename 
 
如果还是有问题: 
那就是文</div>
                                </li>
                                <li><a href="/article/1851.htm"
                                       title="通过ngx-lua来统计nginx上的虚拟主机性能数据" target="_blank">通过ngx-lua来统计nginx上的虚拟主机性能数据</a>
                                    <span class="text-muted">ronin47</span>
<a class="tag" taget="_blank" href="/search/ngx-lua%E3%80%80%E7%BB%9F%E8%AE%A1+%E8%A7%A3%E7%A6%81ip/1.htm">ngx-lua 统计 解禁ip</a>
                                    <div>介绍 
以前我们为nginx做统计,都是通过对日志的分析来完成.比较麻烦,现在基于ngx_lua插件,开发了实时统计站点状态的脚本,解放生产力.项目主页: https://github.com/skyeydemon/ngx-lua-stats 功能 
 
 支持分不同虚拟主机统计, 同一个虚拟主机下可以分不同的location统计. 
 可以统计与query-times request-time </div>
                                </li>
                                <li><a href="/article/1978.htm"
                                       title="java-68-把数组排成最小的数。一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的。例如输入数组{32, 321},则输出32132" target="_blank">java-68-把数组排成最小的数。一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的。例如输入数组{32, 321},则输出32132</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>
import java.util.Arrays;
import java.util.Comparator;

public class MinNumFromIntArray {

	/**
	 * Q68输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。
	 * 例如输入数组{32,  321},则输出这两个能排成的最小数字32132。请给出解决问题</div>
                                </li>
                                <li><a href="/article/2105.htm"
                                       title="Oracle基本操作" target="_blank">Oracle基本操作</a>
                                    <span class="text-muted">ccii</span>
<a class="tag" taget="_blank" href="/search/Oracle+SQL%E6%80%BB%E7%BB%93/1.htm">Oracle SQL总结</a><a class="tag" taget="_blank" href="/search/Oracle+SQL%E8%AF%AD%E6%B3%95/1.htm">Oracle SQL语法</a><a class="tag" taget="_blank" href="/search/Oracle%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/1.htm">Oracle基本操作</a><a class="tag" taget="_blank" href="/search/Oracle+SQL/1.htm">Oracle SQL</a>
                                    <div>一、表操作 
 
1. 常用数据类型 
NUMBER(p,s):可变长度的数字。p表示整数加小数的最大位数,s为最大小数位数。支持最大精度为38位 
NVARCHAR2(size):变长字符串,最大长度为4000字节(以字符数为单位) 
VARCHAR2(size):变长字符串,最大长度为4000字节(以字节数为单位) 
CHAR(size):定长字符串,最大长度为2000字节,最小为1字节,默认</div>
                                </li>
                                <li><a href="/article/2232.htm"
                                       title="[强人工智能]实现强人工智能的路线图" target="_blank">[强人工智能]实现强人工智能的路线图</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
                                    <div> 
    1:创建一个用于记录拓扑网络连接的矩阵数据表 
     2:自动构造或者人工复制一个包含10万个连接(1000*1000)的流程图 
     3:将这个流程图导入到矩阵数据表中 
     4:在矩阵的每个有意义的节点中嵌入一段简单的</div>
                                </li>
                                <li><a href="/article/2359.htm"
                                       title="给Tomcat,Apache配置gzip压缩(HTTP压缩)功能" target="_blank">给Tomcat,Apache配置gzip压缩(HTTP压缩)功能</a>
                                    <span class="text-muted">cwqcwqmax9</span>
<a class="tag" taget="_blank" href="/search/apache/1.htm">apache</a>
                                    <div>背景: 
HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求网页后,从服务器端将网页文件压缩,再下载到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程HTML ,CSS,Javascript , Text ,它可以节省40%左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP , JSP , ASP , Servlet,SHTML等输出的网页也能进行压缩,</div>
                                </li>
                                <li><a href="/article/2486.htm"
                                       title="SpringMVC and Struts2" target="_blank">SpringMVC and Struts2</a>
                                    <span class="text-muted">dashuaifu</span>
<a class="tag" taget="_blank" href="/search/struts2/1.htm">struts2</a><a class="tag" taget="_blank" href="/search/springMVC/1.htm">springMVC</a>
                                    <div>SpringMVC  VS Struts2

1:
spring3开发效率高于struts
2:
spring3 mvc可以认为已经100%零配置
3:
struts2是类级别的拦截, 一个类对应一个request上下文,
springmvc是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应
所以说从架构本身上 spring3 mvc就容易实现r</div>
                                </li>
                                <li><a href="/article/2613.htm"
                                       title="windows常用命令行命令" target="_blank">windows常用命令行命令</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a><a class="tag" taget="_blank" href="/search/cmd/1.htm">cmd</a><a class="tag" taget="_blank" href="/search/command/1.htm">command</a>
                                    <div>在windows系统中,点击开始-运行,可以直接输入命令行,快速打开一些原本需要多次点击图标才能打开的界面,如常用的输入cmd打开dos命令行,输入taskmgr打开任务管理器。此处列出了网上搜集到的一些常用命令。winver 检查windows版本 wmimgmt.msc 打开windows管理体系结构(wmi) wupdmgr windows更新程序 wscrip</div>
                                </li>
                                <li><a href="/article/2740.htm"
                                       title="再看知名应用背后的第三方开源项目" target="_blank">再看知名应用背后的第三方开源项目</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/ios/1.htm">ios</a>
                                    <div>知名应用程序的设计和技术一直都是开发者需要学习的,同样这些应用所使用的开源框架也是不可忽视的一部分。此前《 
iOS第三方开源库的吐槽和备忘》中作者ibireme列举了国内多款知名应用所使用的开源框架,并对其中一些框架进行了分析,同样国外开发者 
@iOSCowboy也在博客中给我们列出了国外多款知名应用使用的开源框架。另外txx's blog中详细介绍了 
Facebook Paper使用的第三</div>
                                </li>
                                <li><a href="/article/2867.htm"
                                       title="Objective-c单例模式的正确写法" target="_blank">Objective-c单例模式的正确写法</a>
                                    <span class="text-muted">jsntghf</span>
<a class="tag" taget="_blank" href="/search/%E5%8D%95%E4%BE%8B/1.htm">单例</a><a class="tag" taget="_blank" href="/search/ios/1.htm">ios</a><a class="tag" taget="_blank" href="/search/iPhone/1.htm">iPhone</a>
                                    <div>一般情况下,可能我们写的单例模式是这样的: 
#import <Foundation/Foundation.h>

@interface Downloader : NSObject

+ (instancetype)sharedDownloader;

@end


#import "Downloader.h"

@implementation</div>
                                </li>
                                <li><a href="/article/2994.htm"
                                       title="jquery easyui datagrid 加载成功,选中某一行" target="_blank">jquery easyui datagrid 加载成功,选中某一行</a>
                                    <span class="text-muted">hae</span>
<a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/easyui/1.htm">easyui</a><a class="tag" taget="_blank" href="/search/datagrid/1.htm">datagrid</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%8A%A0%E8%BD%BD/1.htm">数据加载</a>
                                    <div>1.首先你需要设置datagrid的onLoadSuccess   
$( 
'#dg' 
).datagrid({onLoadSuccess :  
function 
(data){   
     
$( 
'#dg' 
).datagrid( 
'selectRow' 
,3);   
}});       
2.onL</div>
                                </li>
                                <li><a href="/article/3121.htm"
                                       title="jQuery用户数字打分评价效果" target="_blank">jQuery用户数字打分评价效果</a>
                                    <span class="text-muted">ini</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a>
                                    <div>效果体验:http://hovertree.com/texiao/jquery/5.htmHTML文件代码: 
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>jQuery用户数字打分评分代码 - HoverTree</</div>
                                </li>
                                <li><a href="/article/3248.htm"
                                       title="mybatis的paramType" target="_blank">mybatis的paramType</a>
                                    <span class="text-muted">kerryg</span>
<a class="tag" taget="_blank" href="/search/DAO/1.htm">DAO</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a>
                                    <div>MyBatis传多个参数: 
1、采用#{0},#{1}获得参数: 
   Dao层函数方法: 
    public User selectUser(String name,String area); 
 对应的Mapper.xml 
   <select id="selectUser" result</div>
                                </li>
                                <li><a href="/article/3375.htm"
                                       title="centos 7安装mysql5.5" target="_blank">centos 7安装mysql5.5</a>
                                    <span class="text-muted">MrLee23</span>
<a class="tag" taget="_blank" href="/search/centos/1.htm">centos</a>
                                    <div>首先centos7 已经不支持mysql,因为收费了你懂得,所以内部集成了mariadb,而安装mysql的话会和mariadb的文件冲突,所以需要先卸载掉mariadb,以下为卸载mariadb,安装mysql的步骤。 
  
#列出所有被安装的rpm package rpm -qa | grep mariadb 
  
#卸载 
rpm -e mariadb-libs-5.</div>
                                </li>
                                <li><a href="/article/3502.htm"
                                       title="利用thrift来实现消息群发" target="_blank">利用thrift来实现消息群发</a>
                                    <span class="text-muted">qifeifei</span>
<a class="tag" taget="_blank" href="/search/thrift/1.htm">thrift</a>
                                    <div>           Thrift项目一般用来做内部项目接偶用的,还有能跨不同语言的功能,非常方便,一般前端系统和后台server线上都是3个节点,然后前端通过获取client来访问后台server,那么如果是多太server,就是有一个负载均衡的方法,然后最后访问其中一个节点。那么换个思路,能不能发送给所有节点的server呢,如果能就</div>
                                </li>
                                <li><a href="/article/3629.htm"
                                       title="实现一个sizeof获取Java对象大小" target="_blank">实现一个sizeof获取Java对象大小</a>
                                    <span class="text-muted">teasp</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/HotSpot/1.htm">HotSpot</a><a class="tag" taget="_blank" href="/search/%E5%86%85%E5%AD%98/1.htm">内存</a><a class="tag" taget="_blank" href="/search/%E5%AF%B9%E8%B1%A1%E5%A4%A7%E5%B0%8F/1.htm">对象大小</a><a class="tag" taget="_blank" href="/search/sizeof/1.htm">sizeof</a>
                                    <div>   由于Java的设计者不想让程序员管理和了解内存的使用,我们想要知道一个对象在内存中的大小变得比较困难了。本文提供了可以获取对象的大小的方法,但是由于各个虚拟机在内存使用上可能存在不同,因此该方法不能在各虚拟机上都适用,而是仅在hotspot 32位虚拟机上,或者其它内存管理方式与hotspot 32位虚拟机相同的虚拟机上 适用。 
  
  </div>
                                </li>
                                <li><a href="/article/3756.htm"
                                       title="SVN错误及处理" target="_blank">SVN错误及处理</a>
                                    <span class="text-muted">xiangqian0505</span>
<a class="tag" taget="_blank" href="/search/SVN%E6%8F%90%E4%BA%A4%E6%96%87%E4%BB%B6%E6%97%B6%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%BC%BA%E8%A1%8C%E5%85%B3%E9%97%AD/1.htm">SVN提交文件时服务器强行关闭</a>
                                    <div>在SVN服务控制台打开资源库“SVN无法读取current” ---摘自网络 写道   SVN无法读取current修复方法 Can't read file : End of file found 
 
文件:repository/db/txn_current、repository/db/current 
  
其中current记录当前最新版本号,txn_current记录版本库中版本</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>