ES6——异步操作

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> 
 <h4>await</h4> 
 <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>。<br> 另一种情况是,<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>。<br> 顶层的<code>await</code>命令有点像,交出代码的执行权给其他的模块加载,等异步操作完成后,再拿回执行权,继续向下执行。</p> 
</article>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1289288135041163264"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(ES6——异步操作)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1938885213169971200.htm"
                           title="js递归性能优化" target="_blank">js递归性能优化</a>
                        <span class="text-muted">啃火龙果的兔子</span>
<a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91DEMO/1.htm">开发DEMO</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>JavaScript递归性能优化递归是编程中强大的技术,但在JavaScript中如果不注意优化可能会导致性能问题甚至栈溢出。以下是几种优化递归性能的方法:1.尾调用优化(TailCallOptimization,TCO)ES6引入了尾调用优化,但只在严格模式下有效:'usestrict';//普通递归functionfactorial(n){if(n===1)return1;returnn*fa</div>
                    </li>
                    <li><a href="/article/1938603727699111936.htm"
                           title="Reactor Schedulers" target="_blank">Reactor Schedulers</a>
                        <span class="text-muted">lang20150928</span>
<a class="tag" taget="_blank" href="/search/reactor/1.htm">reactor</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>Reactor是一个基于响应式编程的库,它提供了丰富的调度器(Schedulers)机制,用于管理异步操作的执行环境。Schedulers是Reactor中的核心组件之一,它们允许开发者灵活地控制操作符和订阅操作在哪个线程上执行,从而实现高效的并发编程。1.Schedulers的作用Schedulers是Reactor提供的调度器接口,用于定义任务的执行上下文。它们封装了线程管理和调度逻辑,使得开</div>
                    </li>
                    <li><a href="/article/1938577877800906752.htm"
                           title="使用JavaScript构建交互式Web页面:从基础到高级功能(附带实现代码~)" target="_blank">使用JavaScript构建交互式Web页面:从基础到高级功能(附带实现代码~)</a>
                        <span class="text-muted">一ge科研小菜菜</span>
<a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91/1.htm">前端开发</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>个人主页:一ge科研小菜鸡-CSDN博客期待您的关注1.引言JavaScript作为现代网页设计的核心技术之一,已发展成为前端开发中必不可少的编程语言。随着互联网的不断进化,用户对网页体验的需求日益增长,开发者需要借助JavaScript来实现页面动态交互、用户界面更新和复杂的数据操作。JavaScript的生态系统也日趋完善,从基础的ES6+特性到各种强大的前端框架如React、Vue和Angu</div>
                    </li>
                    <li><a href="/article/1938148974213001216.htm"
                           title="Vue3中的watch详解:掌握响应式侦听的艺术" target="_blank">Vue3中的watch详解:掌握响应式侦听的艺术</a>
                        <span class="text-muted"></span>

                        <div>目录一、watch的核心作用二、基础用法(对比Vue2优化)三、高级配置选项四、watchvswatchEffect五、进阶技巧六、最佳实践七、场景示例结语一、watch的核心作用监听响应式数据的变化,在数据变化时执行回调函数,适用于:执行异步操作(如API请求)复杂逻辑处理数据变化时需要联动多个操作的场景二、基础用法(对比Vue2优化)import{ref,watch}from'vue'//1.</div>
                    </li>
                    <li><a href="/article/1937962289294536704.htm"
                           title="JS核心操作符:从基础到ES6+" target="_blank">JS核心操作符:从基础到ES6+</a>
                        <span class="text-muted">Jet_closer</span>
<a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>ECMAScript操作符一、一元操作符1.递增、递减操作符(++/--)2.一元加和减(+/-)二、位操作符1.按位非(~)2.按位与(&)3.按位或(|)4.按位异或(^)5.左移(>)7.无符号右移(>>>)三、布尔操作符1.逻辑非(!)2.逻辑与(&&)3.逻辑或(||)四、算术操作符1.加法操作符(+)2.减法操作符(-)3.乘法操作符(*)4.除法操作符(/)5.取模操作符(%)五、关</div>
                    </li>
                    <li><a href="/article/1937926741045604352.htm"
                           title="Webpack中常用的Loader和Plugin" target="_blank">Webpack中常用的Loader和Plugin</a>
                        <span class="text-muted">aiguangyuan</span>
<a class="tag" taget="_blank" href="/search/Webpack/1.htm">Webpack</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91/1.htm">前端开发</a><a class="tag" taget="_blank" href="/search/Webpack/1.htm">Webpack</a>
                        <div>1.常用的Loader1.1.BabelLoader用于将ES6+代码转译为ES5。示例:module.exports={module:{rules:[{test:/\.js$/,exclude:/node_modules/,use:'babel-loader',},],},};1.2.CSSLoader和StyleLoadercss-loader使你可以使用类似@import和url()的方式实</div>
                    </li>
                    <li><a href="/article/1937848306579337216.htm"
                           title="JS知识点总结三———数组(上)" target="_blank">JS知识点总结三———数组(上)</a>
                        <span class="text-muted">小林同学的前端基地</span>
<a class="tag" taget="_blank" href="/search/JavaScript%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93/1.htm">JavaScript知识点总结</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>数组是JavaScript中最核心的数据结构之一,几乎每个项目都离不开它。本文全面总结JavaScript数组的核心知识,涵盖基础操作、常用方法、性能优化和现代技巧。本文全面总结了JavaScript数组的核心知识,包括创建方式(字面量、构造函数、ES6方法)、数组类型(一维/多维、稀疏数组、类数组对象、类型化数组)、常用方法(增删改查、转换迭代、连接切片等)、属性(length、prototyp</div>
                    </li>
                    <li><a href="/article/1937842380258471936.htm"
                           title="JavaScript 异步操作的深入解析与性能优化" target="_blank">JavaScript 异步操作的深入解析与性能优化</a>
                        <span class="text-muted"></span>

                        <div>JavaScript异步操作的深入解析与性能优化理解JavaScript异步操作的运行机制,需要深入掌握事件循环(EventLoop)、调用栈(CallStack)、任务队列(TaskQueue)等核心概念。这些机制共同协作,使单线程的JavaScript能够高效处理异步任务。一、JavaScript执行环境的基础组件1.1调用栈(CallStack)调用栈是JavaScript引擎执行代码的核心</div>
                    </li>
                    <li><a href="/article/1937641148898144256.htm"
                           title="测试框架重试与跳过机制" target="_blank">测试框架重试与跳过机制</a>
                        <span class="text-muted">追逐此刻</span>
<a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>作为测试架构师,针对测试框架中的重试和跳过机制,我将分别设计一个复杂场景及实现逻辑,并提供具体实例说明。一、重试问题:分布式环境下的幂等性验证重试复杂场景:在微服务架构中,当测试用例涉及跨服务的异步操作(如订单支付流程)时,可能出现:服务间通信超时但实际操作已执行最终一致性导致状态延迟需要验证分布式事务的幂等性框架层设计逻辑:classDistributedRetryPolicy:def__ini</div>
                    </li>
                    <li><a href="/article/1937436754386677760.htm"
                           title="Android 异步操作库 RxJava" target="_blank">Android 异步操作库 RxJava</a>
                        <span class="text-muted">Just_Paranoid</span>
<a class="tag" taget="_blank" href="/search/Android/1.htm">Android</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/rxjava/1.htm">rxjava</a><a class="tag" taget="_blank" href="/search/rxandroid/1.htm">rxandroid</a>
                        <div>RxJava概述RxJava是一种响应式编程,来创建基于事件的异步操作库。基于事件流的链式调用、逻辑清晰简洁。RxJava我的理解是将事件从起点(上游)流向终点(下游),中间有很多卡片对数据进操作并传递,每个卡片获取上一个卡片传递下来的结果然后对事件进行处理然后将结果传递给下一个卡片,这样事件就从起点通过卡片一次次传递直到流向终点。RxJava观察者模式传统观察者是一个被观察者多过观察者,当被观察</div>
                    </li>
                    <li><a href="/article/1937435367909158912.htm"
                           title="利用RxJava实现Android异步编程的深入探索" target="_blank">利用RxJava实现Android异步编程的深入探索</a>
                        <span class="text-muted">爱分析</span>
<a class="tag" taget="_blank" href="/search/RxJava/1.htm">RxJava</a><a class="tag" taget="_blank" href="/search/Android%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B/1.htm">Android异步编程</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/%E8%BF%9B%E5%BA%A6%E6%98%BE%E7%A4%BA/1.htm">进度显示</a><a class="tag" taget="_blank" href="/search/UI%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86/1.htm">UI事件处理</a>
                        <div>利用RxJava实现Android异步编程的深入探索背景简介RxJava作为响应式编程的一个重要工具,对于Android开发者来说,它极大地简化了异步操作的处理。通过本文,我们将深入探讨RxJava在Android平台中如何实现网络操作的异步处理,并且记录进度,以及如何处理UI事件。实现网络操作的异步处理和进度记录在Android开发中,网络操作通常需要在后台线程中进行,以避免阻塞主线程导致UI卡</div>
                    </li>
                    <li><a href="/article/1937363268167331840.htm"
                           title="Vue2中Vuex的五种核心状态管理详解:从State到Modules" target="_blank">Vue2中Vuex的五种核心状态管理详解:从State到Modules</a>
                        <span class="text-muted">上单带刀不带妹</span>
<a class="tag" taget="_blank" href="/search/Vue/1.htm">Vue</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><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a>
                        <div>目录一、为什么需要Vuex?二、Vuex核心概念图解编辑三、五种核心状态详解1.State:数据仓库2.Getters:计算属性3.Mutations:同步修改器4.Actions:异步操作5.Modules:模块化四、各概念关系总结五、最佳实践技巧结语一、为什么需要Vuex?当组件层级变深、兄弟组件需要共享数据时,传统的props/$emit和事件总线会变得难以维护。Vuex通过集中式存储管理应</div>
                    </li>
                    <li><a href="/article/1937247050819235840.htm"
                           title="生成器函数概念与用法详解" target="_blank">生成器函数概念与用法详解</a>
                        <span class="text-muted">teeeeeeemo</span>
<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%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>生成器函数(GeneratorFunction)是JavaScript中一种特殊的函数,它允许你逐步执行函数体,并在过程中多次返回(或“产出”)值,而不是一次性执行完毕。这种特性使得它非常适合处理异步操作、创建迭代器和处理大数据集等场景。核心概念语法标记使用function*声明(注意星号*的位置):function*myGenerator(){//函数体}yield关键字在函数内部用yield暂</div>
                    </li>
                    <li><a href="/article/1937128429765849088.htm"
                           title="Web前端入门:JavaScript 运算符 == 和 === 有什么区别?" target="_blank">Web前端入门:JavaScript 运算符 == 和 === 有什么区别?</a>
                        <span class="text-muted">液态不合群</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/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>运算符JavaScript运算符是真的多,尤其是ES6之后还在不停的加运算符,其他编程语言看JS就像怪物一样,各种骚操作不断~~运算符分类1、算术运算符算术运算符的作用就是用来基础计算,跟小学课本一样,包含:加+,减-,乘*,除/,取余(也叫做取模)%,指数**,自增++,自减--。只是需注意:乘号不再是x,除号也不再是÷!与我们学过的运算法则一样,乘法与除法优先级比加减法高,如果要改变优先级,需</div>
                    </li>
                    <li><a href="/article/1937054165951901696.htm"
                           title="Ajax 核心知识点全面总结" target="_blank">Ajax 核心知识点全面总结</a>
                        <span class="text-muted">YD_1989</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%9F%BA%E7%A1%80/1.htm">前端基础</a><a class="tag" taget="_blank" href="/search/%E5%BE%AE%E6%9C%8D%E5%8A%A1/1.htm">微服务</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/ajax/1.htm">ajax</a><a class="tag" taget="_blank" href="/search/okhttp/1.htm">okhttp</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>文章目录Ajax核心知识点全面总结一、Ajax基础概念1、定义2、核心特点二、Ajax工作原理与核心组件1、工作流程2、XMLHttpRequest(XHR)对象三、Ajax请求方法与参数1、常见请求方法2、请求参数处理四、Ajax异步与错误处理1、异步处理2、错误处理五、跨域资源共享(CORS)与解决方案1、跨域问题2、解决方案六、Ajax与现代替代方案1、FetchAPI(ES6+)2、Axi</div>
                    </li>
                    <li><a href="/article/1937009921715400704.htm"
                           title="JavaScript箭头函数:简洁语法背后的哲学与实践" target="_blank">JavaScript箭头函数:简洁语法背后的哲学与实践</a>
                        <span class="text-muted">coding随想</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>JavaScript箭头函数:简洁语法背后的哲学与实践引言JavaScript作为一门动态语言,随着ES6(ECMAScript2015)的推出,迎来了许多革命性的特性。其中,箭头函数(ArrowFunction)无疑是开发者最常使用的工具之一。它不仅简化了代码的书写方式,还彻底改变了JavaScript中this的绑定规则。本文将从语法、特性、使用场景到哲学意义,深入浅出地解析箭头函数的魅力与陷</div>
                    </li>
                    <li><a href="/article/1936795358025805824.htm"
                           title="JavaScript中的反射魔法:揭秘Reflect对象的核心方法(上)" target="_blank">JavaScript中的反射魔法:揭秘Reflect对象的核心方法(上)</a>
                        <span class="text-muted">coding随想</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/proxy%E6%A8%A1%E5%BC%8F/1.htm">proxy模式</a>
                        <div>JavaScript中的反射魔法:揭秘Reflect对象的核心方法在JavaScript的世界中,反射(Reflection)是一种强大的机制,它允许我们以程序化的方式操作对象的属性和行为。ES6引入的Reflect对象,为开发者提供了统一的API来实现这一目标。通过Reflect,我们可以更优雅地处理对象属性的访问、修改、删除等操作,甚至与Proxy结合实现高级功能。本文将深入解析Reflect</div>
                    </li>
                    <li><a href="/article/1936289248609103872.htm"
                           title="uni-app项目实战笔记17--获取系统信息getSystemInfo状态栏和胶囊按钮" target="_blank">uni-app项目实战笔记17--获取系统信息getSystemInfo状态栏和胶囊按钮</a>
                        <span class="text-muted">moxiaoran5753</span>
<a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a>
                        <div>接着上一篇笔记,在添加头部导航栏后,H5显示正常:但在微信小程序中,由于刘海屏的存在,添加的头部导航栏跟状态栏重叠在一起:因此需要获取状态栏的高度以便状态栏和导航栏错开不重叠在一起。同时头部导航栏通过与右侧的胶囊按钮在同一水平线上,因此也需要获取胶囊按钮的高度来控制显示。uniapp提供了获取系统信息的API:getSystemInfo(异步操作),详情可访问uniapp官方文档:系统信息的概念|</div>
                    </li>
                    <li><a href="/article/1936272747541884928.htm"
                           title="VUE vuex深入浅出" target="_blank">VUE vuex深入浅出</a>
                        <span class="text-muted">武昌库里写JAVA</span>
<a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%B1%87%E6%80%BB%E4%B8%8E%E8%A7%A3%E6%9E%90/1.htm">面试题汇总与解析</a><a class="tag" taget="_blank" href="/search/%E8%AF%BE%E7%A8%8B%E8%AE%BE%E8%AE%A1/1.htm">课程设计</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/layui/1.htm">layui</a><a class="tag" taget="_blank" href="/search/%E6%AF%95%E4%B8%9A%E8%AE%BE%E8%AE%A1/1.htm">毕业设计</a>
                        <div>image.png①state:定义初始数据。②mutations:更改Vuex的store中的状态的唯一方法是提交mutation③getters:可以对state进行计算操作,它就是store的计算属性虽然在组件内也可以做计算属性,但是getters可以在多给件之间复用如果一个状态只在一个组件内使用,是可以不用getters。④actions:异步操作初始数据,其实就是调用mutations里</div>
                    </li>
                    <li><a href="/article/1936118329039187968.htm"
                           title="每天一个前端小知识 Day 3 - JavaScript 的作用域与闭包" target="_blank">每天一个前端小知识 Day 3 - JavaScript 的作用域与闭包</a>
                        <span class="text-muted">蓝婷儿</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/1.htm">前端面试</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>JavaScript的作用域与闭包1.什么是作用域(Scope)?作用域是变量定义的可访问范围。分类:类型说明全局作用域在任何地方都能访问的变量(如浏览器中window)函数作用域函数内部定义的变量只能在函数内部访问块级作用域(ES6)使用let、const创建的变量,限制在{}中示例:vara=1;//全局作用域functionfoo(){varb=2;//函数作用域if(true){letc=</div>
                    </li>
                    <li><a href="/article/1936051629694447616.htm"
                           title="异步编程中std::async的使用" target="_blank">异步编程中std::async的使用</a>
                        <span class="text-muted">lianghu666</span>
<a class="tag" taget="_blank" href="/search/C%2FC%2B%2B/1.htm">C/C++</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a>
                        <div>std::async是一个C++标准库中的函数模板,用于异步执行可调用对象(例如函数、lambda表达式或函数对象)。它提供了一种简便的方式来启动异步操作,并通过std::future对象获取异步操作的结果。下面我将从简单到复杂,一步一步详细解释std::async的用法和相关概念。1.基本概念std::async的主要作用是异步执行一个任务,并返回一个std::future对象,通过这个对象可以</div>
                    </li>
                    <li><a href="/article/1935714510958555136.htm"
                           title="React状态管理——react-redux" target="_blank">React状态管理——react-redux</a>
                        <span class="text-muted"></span>

                        <div>目录一、redux介绍二、安装三、基本实现步骤3.1创建ActionTypes3.2创建counterAction3.3创建counterReducer3.4结合所有Reducer3.5创建store3.6入口文件中提供store3.7在组件中的使用3.8使用thunk实现异步支持3.8.1安装3.8.2在counterAction中添加异步操作3.8.3在store文件中添加异步支持3.8.4在</div>
                    </li>
                    <li><a href="/article/1935321154101243904.htm"
                           title="webpack es6 npm包_前端 Webpack 工程化的最佳实践" target="_blank">webpack es6 npm包_前端 Webpack 工程化的最佳实践</a>
                        <span class="text-muted"></span>

                        <div>作者|阿里文娱前端开发专家芃苏责编|屠敏头图|CSDN下载自视觉中国引言▐前端构建工具的演变回想在2015-2016年的时候,开发者们开始渐渐把视线从大量使用TaskRunner的Grunt工具,转移到Gulp这种Pipeline形式的工具。Gulp还可以配合上众多个性化插件(如gulp-streamify),从而使得整个前端的准备工作链路,变得清晰易控,如刷新页面、代码的编译和压缩等等。自动化“</div>
                    </li>
                    <li><a href="/article/1934948379247308800.htm"
                           title="分享在日常开发中常用的ES6知识点【面试常考】" target="_blank">分享在日常开发中常用的ES6知识点【面试常考】</a>
                        <span class="text-muted">十八朵郁金香</span>
<a class="tag" taget="_blank" href="/search/JavaScript%E4%BF%AE%E8%A1%8C/1.htm">JavaScript修行</a><a class="tag" taget="_blank" href="/search/es6/1.htm">es6</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>前言在日常的业务开发中,可以熟悉运用掌握的知识点快速解决问题很重要。这篇分享JS相关的知识点,主要就是对数据的处理。注意:本篇分享的知识点,只是起到一个抛砖引玉的作用,详情的使用和更多的ES6知识点还请参考官网。1.箭头函数特点:简洁语法,不绑定this、arguments。应用场景:回调函数、数组方法(如map、filter)。//传统函数constlist=[1,2,3];constdoubl</div>
                    </li>
                    <li><a href="/article/1934910936842301440.htm"
                           title="Flutter setState() 状态管理详细使用指南" target="_blank">Flutter setState() 状态管理详细使用指南</a>
                        <span class="text-muted">帅次</span>
<a class="tag" taget="_blank" href="/search/Flutter/1.htm">Flutter</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</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><a class="tag" taget="_blank" href="/search/%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">小程序</a><a class="tag" taget="_blank" href="/search/kotlin/1.htm">kotlin</a><a class="tag" taget="_blank" href="/search/flutter/1.htm">flutter</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/studio/1.htm">studio</a>
                        <div>目录一、什么是setState()二、setState()的基本用法2.1基本计数器示例2.2工作原理三、setState()的最佳实践3.1只在需要时调用3.2最小化setState范围3.3避免在build方法中调用3.4处理异步操作四、setState()的高级用法4.1与动画结合4.2复杂对象的状态管理五、setState()的局限性六、何时使用setState()七、总结相关推荐一、什么</div>
                    </li>
                    <li><a href="/article/1934381839183572992.htm"
                           title="【JS-2】JavaScript基础语法完全指南:从入门到精通" target="_blank">【JS-2】JavaScript基础语法完全指南:从入门到精通</a>
                        <span class="text-muted"></span>

                        <div>JavaScript作为现代Web开发的基石语言,掌握其基础语法是每位开发者的必经之路。本文将系统性地介绍JavaScript的核心语法概念,帮助您构建坚实的编程基础。1.变量与数据类型1.1变量声明JavaScript提供了三种变量声明方式://ES5varname="张三";//函数作用域//ES6+letage=25;//块级作用域constPI=3.14;//块级作用域常量最佳实践:默认使</div>
                    </li>
                    <li><a href="/article/1933926010760261632.htm"
                           title="ES6——对象扩展之Set对象" target="_blank">ES6——对象扩展之Set对象</a>
                        <span class="text-muted">Aotman_</span>
<a class="tag" taget="_blank" href="/search/es6/1.htm">es6</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</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>在ES6(ECMAScript2015)中,Set对象允许存储任何类型的唯一值,无论是原始值还是对象引用。Set对象有一些有用的方法,可以操作集合中的数据。以下是一些常用的Set对象方法:方法描述add向Set对象添加一个元素。如果元素已存在,则不会添加。delete删除Set对象中的元素。如果元素不存在,则返回false。has返回一个布尔值,表示Set对象中是否存在该元素。clear清除Set</div>
                    </li>
                    <li><a href="/article/1933903805368299520.htm"
                           title="前端JavaScript面试题(2)" target="_blank">前端JavaScript面试题(2)</a>
                        <span class="text-muted">哈哈小鬼~FE</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">前端面试题</a><a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/JS%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">JS面试题</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><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.箭头函数的this指向哪里?3.扩展运算符的作用及使用场景?4.对对象\数组解构的理解?5.你是怎么理解ES6中Proxy的?使用场景有哪些?6.说说对ES6中rest参数的理解?7.Map和Object的区别?8.Map和WeakMap的区别?9.JavaScript有哪些内置对象?10.正则表达式运用及使用场景?11.JavaScript脚本延迟加载</div>
                    </li>
                    <li><a href="/article/1933742175846199296.htm"
                           title="Vue3 Hooks 完全指南:从基础用法到异步操作实践" target="_blank">Vue3 Hooks 完全指南:从基础用法到异步操作实践</a>
                        <span class="text-muted">꒰ঌ小武໒꒱</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a><a class="tag" taget="_blank" href="/search/typescript/1.htm">typescript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>一、引言:为什么需要Vue3Hooks?在Vue3中,CompositionAPI的引入带来了全新的组件开发模式。Hooks(通常以use开头的函数)作为CompositionAPI的核心特性,允许开发者以函数形式组织和复用组件逻辑,彻底改变了传统OptionsAPI中基于配置对象的开发方式。相比之下,Hooks具有以下显著优势:逻辑复用更灵活:告别mixin的命名冲突问题,通过函数封装实现细粒度</div>
                    </li>
                    <li><a href="/article/1933646525112578048.htm"
                           title="Vuex 中Mutation 和Action介绍" target="_blank">Vuex 中Mutation 和Action介绍</a>
                        <span class="text-muted">yuren_xia</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E6%8A%80%E6%9C%AF/1.htm">前端技术</a><a class="tag" taget="_blank" href="/search/vuex/1.htm">vuex</a>
                        <div>在Vuex中,Mutation(变更)和Action(动作)都是修改状态的核心机制,但职责和使用场景不同:Mutation(变更)核心特性:同步操作:必须是同步函数(直接修改状态)。直接修改状态:唯一能修改Vuexstate的方式。可追踪性:每个mutation会被开发工具记录,便于调试时间旅行(state快照)。不可异步:不能在mutation中执行异步操作(否则状态变更无法追踪)。定义方式:/</div>
                    </li>
                                <li><a href="/article/85.htm"
                                       title="ztree异步加载" target="_blank">ztree异步加载</a>
                                    <span class="text-muted">3213213333332132</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/ztree/1.htm">ztree</a>
                                    <div>相信新手用ztree的时候,对异步加载会有些困惑,我开始的时候也是看了API花了些时间才搞定了异步加载,在这里分享给大家。 
我后台代码生成的是json格式的数据,数据大家按各自的需求生成,这里只给出前端的代码。 
 
设置setting,这里只关注async属性的配置 
 

            var setting = {
            	//异步加载配置	
      </div>
                                </li>
                                <li><a href="/article/212.htm"
                                       title="thirft rpc 具体调用流程" target="_blank">thirft rpc 具体调用流程</a>
                                    <span class="text-muted">BlueSkator</span>
<a class="tag" taget="_blank" href="/search/%E4%B8%AD%E9%97%B4%E4%BB%B6/1.htm">中间件</a><a class="tag" taget="_blank" href="/search/rpc/1.htm">rpc</a><a class="tag" taget="_blank" href="/search/thrift/1.htm">thrift</a>
                                    <div>Thrift调用过程中,Thrift客户端和服务器之间主要用到传输层类、协议层类和处理类三个主要的核心类,这三个类的相互协作共同完成rpc的整个调用过程。在调用过程中将按照以下顺序进行协同工作: 
        (1)     将客户端程序调用的函数名和参数传递给协议层(TProtocol),协议</div>
                                </li>
                                <li><a href="/article/339.htm"
                                       title="异或运算推导, 交换数据" target="_blank">异或运算推导, 交换数据</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a><a class="tag" taget="_blank" href="/search/%E5%BC%82%E6%88%96/1.htm">异或</a><a class="tag" taget="_blank" href="/search/%5E/1.htm">^</a>
                                    <div>/*
 * 5 0101
 * 9 1010
 *
 * 5 ^ 5
 * 0101
 * 0101
 * -----
 * 0000
 * 得出第一个规律: 相同的数进行异或, 结果是0
 *
 * 9 ^ 5 ^ 6
 * 1010
 * 0101
 * ----
 * 1111
 *
 * 1111
 * 0110
 * ----
 * 1001
 </div>
                                </li>
                                <li><a href="/article/466.htm"
                                       title="事件源对象" target="_blank">事件源对象</a>
                                    <span class="text-muted">周华华</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml&q</div>
                                </li>
                                <li><a href="/article/593.htm"
                                       title="MySql配置及相关命令" target="_blank">MySql配置及相关命令</a>
                                    <span class="text-muted">g21121</span>
<a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a>
                                    <div>        MySQL安装完毕后我们需要对它进行一些设置及性能优化,主要包括字符集设置,启动设置,连接优化,表优化,分区优化等等。 
  
        一 修改MySQL密码及用户 
     </div>
                                </li>
                                <li><a href="/article/720.htm"
                                       title="[简单]poi删除excel 2007超链接" target="_blank">[简单]poi删除excel 2007超链接</a>
                                    <span class="text-muted">53873039oycg</span>
<a class="tag" taget="_blank" href="/search/Excel/1.htm">Excel</a>
                                    <div>      采用解析sheet.xml方式删除超链接,缺点是要打开文件2次,代码如下: 
     
public void removeExcel2007AllHyperLink(String filePath) throws Exception {
		OPCPackage ocPkg = OPCPac</div>
                                </li>
                                <li><a href="/article/847.htm"
                                       title="Struts2添加 open flash chart" target="_blank">Struts2添加 open flash chart</a>
                                    <span class="text-muted">云端月影</span>

                                    <div>准备以下开源项目: 
1. Struts 2.1.6 
2. Open Flash Chart 2 Version 2 Lug Wyrm Charmer (28th, July 2009) 
3. jofc2,这东西不知道是没做好还是什么意思,好像和ofc2不怎么匹配,最好下源码,有什么问题直接改。 
4. log4j 
 
用eclipse新建动态网站,取名OFC2Demo,将Struts2 l</div>
                                </li>
                                <li><a href="/article/974.htm"
                                       title="spring包详解" target="_blank">spring包详解</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                                    <div>  
下载的spring包中文件及各种包众多,在项目中往往只有部分是我们必须的,如果不清楚什么时候需要什么包的话,看看下面就知道了。 aspectj目录下是在Spring框架下使用aspectj的源代码和测试程序文件。Aspectj是java最早的提供AOP的应用框架。 dist 目录下是Spring 的发布包,关于发布包下面会详细进行说明。 docs&nb</div>
                                </li>
                                <li><a href="/article/1101.htm"
                                       title="网站推广之seo概念" target="_blank">网站推广之seo概念</a>
                                    <span class="text-muted">antonyup_2006</span>
<a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/%E5%BA%94%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">应用服务器</a><a class="tag" taget="_blank" href="/search/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1.htm">搜索引擎</a><a class="tag" taget="_blank" href="/search/Google/1.htm">Google</a>
                                    <div>   持续开发一年多的b2c网站终于在08年10月23日上线了。作为开发人员的我在修改bug的同时,准备了解下网站的推广分析策略。 
    所谓网站推广,目的在于让尽可能多的潜在用户了解并访问网站,通过网站获得有关产品和服务等信息,为最终形成购买决策提供支持。 
    网站推广策略有很多,seo,email,adv</div>
                                </li>
                                <li><a href="/article/1228.htm"
                                       title="单例模式,sql注入,序列" target="_blank">单例模式,sql注入,序列</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/1.htm">单例模式</a><a class="tag" taget="_blank" href="/search/%E5%BA%8F%E5%88%97/1.htm">序列</a><a class="tag" taget="_blank" href="/search/sql%E6%B3%A8%E5%85%A5/1.htm">sql注入</a><a class="tag" taget="_blank" href="/search/%E9%A2%84%E7%BC%96%E8%AF%91/1.htm">预编译</a>
                                    <div>  
序列在前面写过有关的博客,也有过总结,但是今天在做一个JDBC操作数据库的相关内容时 需要使用序列创建一个自增长的字段  居然不会了,所以将序列写在本篇的前面 
  
 1,序列是一个保存数据连续的增长的一种方式; 
序列的创建; 
 CREATE SEQUENCE seq_pro
  2  INCREMENT BY 1 -- 每次加几个
  3 </div>
                                </li>
                                <li><a href="/article/1355.htm"
                                       title="Mockito单元测试实例" target="_blank">Mockito单元测试实例</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/1.htm">单元测试</a><a class="tag" taget="_blank" href="/search/mockito/1.htm">mockito</a>
                                    <div>Mockito单元测试实例: 
public class SettingServiceTest {
    
    private List<PersonDTO> personList = new ArrayList<PersonDTO>();
    
    @InjectMocks
    private SettingPojoService settin</div>
                                </li>
                                <li><a href="/article/1482.htm"
                                       title="精通Oracle10编程SQL(9)使用游标" target="_blank">精通Oracle10编程SQL(9)使用游标</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/plsql/1.htm">plsql</a>
                                    <div>/*
 *使用游标
 */
--显示游标
--在显式游标中使用FETCH...INTO语句
DECLARE
   CURSOR emp_cursor is 
      select ename,sal from emp where deptno=1;
   v_ename emp.ename%TYPE;
   v_sal emp.sal%TYPE;
begin
   ope</div>
                                </li>
                                <li><a href="/article/1609.htm"
                                       title="【Java语言】动态代理" target="_blank">【Java语言】动态代理</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/java%E8%AF%AD%E8%A8%80/1.htm">java语言</a>
                                    <div>  JDK接口动态代理 
JDK自带的动态代理通过动态的根据接口生成字节码(实现接口的一个具体类)的方式,为接口的实现类提供代理。被代理的对象和代理对象通过InvocationHandler建立关联 
  
package com.tom;

import com.tom.model.User;
import com.tom.service.IUserService;
</div>
                                </li>
                                <li><a href="/article/1736.htm"
                                       title="Java通信之URL通信基础" target="_blank">Java通信之URL通信基础</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/jdk/1.htm">jdk</a><a class="tag" taget="_blank" href="/search/webservice/1.htm">webservice</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/1.htm">网络协议</a><a class="tag" taget="_blank" href="/search/ITeye/1.htm">ITeye</a>
                                    <div>java对网络通信以及提供了比较全面的jdk支持,java.net包能让程序员直接在程序中实现网络通信。 
在技术日新月异的现在,我们能通过很多方式实现数据通信,比如webservice、url通信、socket通信等等,今天简单介绍下URL通信。 
学习准备:建议首先学习java的IO基础知识 
  
URL是统一资源定位器的简写,URL可以访问Internet和www,可以通过url</div>
                                </li>
                                <li><a href="/article/1863.htm"
                                       title="博弈Java讲义 - Java线程同步 (1)" target="_blank">博弈Java讲义 - Java线程同步 (1)</a>
                                    <span class="text-muted">boyitech</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/%E5%90%8C%E6%AD%A5/1.htm">同步</a><a class="tag" taget="_blank" href="/search/%E9%94%81/1.htm">锁</a>
                                    <div>  
在并发编程中经常会碰到多个执行线程共享资源的问题。例如多个线程同时读写文件,共用数据库连接,全局的计数器等。如果不处理好多线程之间的同步问题很容易引起状态不一致或者其他的错误。 
   同步不仅可以阻止一个线程看到对象处于不一致的状态,它还可以保证进入同步方法或者块的每个线程,都看到由同一锁保护的之前所有的修改结果。处理同步的关键就是要正确的识别临界条件(cri</div>
                                </li>
                                <li><a href="/article/1990.htm"
                                       title="java-给定字符串,删除开始和结尾处的空格,并将中间的多个连续的空格合并成一个。" target="_blank">java-给定字符串,删除开始和结尾处的空格,并将中间的多个连续的空格合并成一个。</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>

public class DeleteExtraSpace {

	/**
	 * 题目:给定字符串,删除开始和结尾处的空格,并将中间的多个连续的空格合并成一个。
	 * 方法1.用已有的String类的trim和replaceAll方法
	 * 方法2.全部用正则表达式,这个我不熟
	 * 方法3.“重新发明轮子”,从头遍历一次
	 */
	public static v</div>
                                </li>
                                <li><a href="/article/2117.htm"
                                       title="An error has occurred.See the log file错误解决!" target="_blank">An error has occurred.See the log file错误解决!</a>
                                    <span class="text-muted">Kai_Ge</span>
<a class="tag" taget="_blank" href="/search/MyEclipse/1.htm">MyEclipse</a>
                                    <div>今天早上打开MyEclipse时,自动关闭!弹出An error has occurred.See the log file错误提示! 
很郁闷昨天启动和关闭还好着!!!打开几次依然报此错误,确定不是眼花了! 
打开日志文件!找到当日错误文件内容: 
--------------------------------------------------------------------------</div>
                                </li>
                                <li><a href="/article/2244.htm"
                                       title="[矿业与工业]修建一个空间矿床开采站要多少钱?" target="_blank">[矿业与工业]修建一个空间矿床开采站要多少钱?</a>
                                    <span class="text-muted">comsci</span>

                                    <div> 
       地球上的钛金属矿藏已经接近枯竭........... 
 
       我们在冥王星的一颗卫星上面发现一些具有开采价值的矿床..... 
 
       那么,现在要编制一个预算,提交给财政部门..</div>
                                </li>
                                <li><a href="/article/2371.htm"
                                       title="解析Google Map Routes" target="_blank">解析Google Map Routes</a>
                                    <span class="text-muted">dai_lm</span>
<a class="tag" taget="_blank" href="/search/google+api/1.htm">google api</a>
                                    <div>为了获得从A点到B点的路劲,经常会使用Google提供的API,例如 
[url] 
http://maps.googleapis.com/maps/api/directions/json?origin=40.7144,-74.0060&destination=47.6063,-122.3204&sensor=false 
[/url] 
从返回的结果上,大致可以了解应该怎么走,但</div>
                                </li>
                                <li><a href="/article/2498.htm"
                                       title="SQL还有多少“理所应当”?" target="_blank">SQL还有多少“理所应当”?</a>
                                    <span class="text-muted">datamachine</span>
<a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a>
                                    <div>转贴存档,原帖地址:http://blog.chinaunix.net/uid-29242841-id-3968998.html、http://blog.chinaunix.net/uid-29242841-id-3971046.html! 
 
------------------------------------华丽的分割线-------------------------------- 
</div>
                                </li>
                                <li><a href="/article/2625.htm"
                                       title="Yii使用Ajax验证时,如何设置某些字段不需要验证" target="_blank">Yii使用Ajax验证时,如何设置某些字段不需要验证</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a><a class="tag" taget="_blank" href="/search/yii/1.htm">yii</a>
                                    <div>经常像你注册页面,你可能非常希望只需要Ajax去验证用户名和Email,而不需要使用Ajax再去验证密码,默认如果你使用Yii 内置的ajax验证Form,例如: 
$form=$this->beginWidget('CActiveForm', array(        'id'=>'usuario-form',&</div>
                                </li>
                                <li><a href="/article/2752.htm"
                                       title="使用git同步网站代码" target="_blank">使用git同步网站代码</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/crontab/1.htm">crontab</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a>
                                    <div>转自:http://ued.ctrip.com/blog/?p=3646?tn=gongxinjun.com 
  
管理一网站,最开始使用的虚拟空间,采用提供商支持的ftp上传网站文件,后换用vps,vps可以自己搭建ftp的,但是懒得搞,直接使用scp传输文件到服务器,现在需要更新文件到服务器,使用scp真的很烦。发现本人就职的公司,采用的git+rsync的方式来管理、同步代码,遂</div>
                                </li>
                                <li><a href="/article/2879.htm"
                                       title="sql基本操作" target="_blank">sql基本操作</a>
                                    <span class="text-muted">蕃薯耀</span>
<a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/sql%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/1.htm">sql基本操作</a><a class="tag" taget="_blank" href="/search/sql%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/1.htm">sql常用操作</a>
                                    <div>sql基本操作 
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
蕃薯耀 2015年6月1日 17:30:33 星期一 
  
  
&</div>
                                </li>
                                <li><a href="/article/3006.htm"
                                       title="Spring4+Hibernate4+Atomikos3.3多数据源事务管理" target="_blank">Spring4+Hibernate4+Atomikos3.3多数据源事务管理</a>
                                    <span class="text-muted">hanqunfeng</span>
<a class="tag" taget="_blank" href="/search/Hibernate4/1.htm">Hibernate4</a>
                                    <div>Spring3+后不再对JTOM提供支持,所以可以改用Atomikos管理多数据源事务。Spring2.5+Hibernate3+JTOM参考:http://hanqunfeng.iteye.com/blog/1554251Atomikos官网网站:http://www.atomikos.com/   一.pom.xml 
<dependency>
			<</div>
                                </li>
                                <li><a href="/article/3133.htm"
                                       title="jquery中两个值得注意的方法one()和trigger()方法" target="_blank">jquery中两个值得注意的方法one()和trigger()方法</a>
                                    <span class="text-muted">jackyrong</span>
<a class="tag" taget="_blank" href="/search/trigger/1.htm">trigger</a>
                                    <div>  在jquery中,有两个值得注意但容易忽视的方法,分别是one()方法和trigger()方法,这是从国内作者<<jquery权威指南》一书中看到不错的介绍 
 
 
1) one方法 
    one方法的功能是让所选定的元素绑定一个仅触发一次的处理函数,格式为 
   one(type,${data},fn) 
&nb</div>
                                </li>
                                <li><a href="/article/3260.htm"
                                       title="拿工资不仅仅是让你写代码的" target="_blank">拿工资不仅仅是让你写代码的</a>
                                    <span class="text-muted">lampcy</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%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%92%A8%E8%AF%A2/1.htm">咨询</a>
                                    <div>这是我对团队每个新进员工说的第一件事情。这句话的意思是,我并不关心你是如何快速完成任务的,哪怕代码很差,只要它像救生艇通气门一样管用就行。这句话也是我最喜欢的座右铭之一。 
这个说法其实很合理:我们的工作是思考客户提出的问题,然后制定解决方案。思考第一,代码第二,公司请我们的最终目的不是写代码,而是想出解决方案。 
话粗理不粗。 
付你薪水不是让你来思考的,也不是让你来写代码的,你的目的是交付产品</div>
                                </li>
                                <li><a href="/article/3387.htm"
                                       title="架构师之对象操作----------对象的效率复制和判断是否全为空" target="_blank">架构师之对象操作----------对象的效率复制和判断是否全为空</a>
                                    <span class="text-muted">nannan408</span>
<a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84%E5%B8%88/1.htm">架构师</a>
                                    <div>1.前言。 
  如题。 
2.代码。 
 (1)对象的复制,比spring的beanCopier在大并发下效率要高,利用net.sf.cglib.beans.BeanCopier 
 

Src src=new Src();
BeanCopier beanCopier = BeanCopier.create(Src.class, Des.class, false);
      </div>
                                </li>
                                <li><a href="/article/3514.htm"
                                       title="ajax 被缓存的解决方案" target="_blank">ajax 被缓存的解决方案</a>
                                    <span class="text-muted">Rainbow702</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a><a class="tag" taget="_blank" href="/search/cache/1.htm">cache</a><a class="tag" taget="_blank" href="/search/%E7%BC%93%E5%AD%98/1.htm">缓存</a>
                                    <div>使用jquery的ajax来发送请求进行局部刷新画面,各位可能都做过。 
今天碰到一个奇怪的现象,就是,同一个ajax请求,在chrome中,不论发送多少次,都可以发送至服务器端,而不会被缓存。但是,换成在IE下的时候,发现,同一个ajax请求,会发生被缓存的情况,只有第一次才会被发送至服务器端,之后的不会再被发送。郁闷。 
解决方法如下: 
① 直接使用 JQuery提供的 “cache”参数,</div>
                                </li>
                                <li><a href="/article/3641.htm"
                                       title="修改date.toLocaleString()的警告" target="_blank">修改date.toLocaleString()的警告</a>
                                    <span class="text-muted">tntxia</span>
<a class="tag" taget="_blank" href="/search/String/1.htm">String</a>
                                    <div>  
我们在写程序的时候,经常要查看时间,所以我们经常会用到date.toLocaleString(),但是date.toLocaleString()是一个过时 的API,代替的方法如下: 
  
package com.tntxia.htmlmaker.util;

import java.text.SimpleDateFormat;
import java.util.</div>
                                </li>
                                <li><a href="/article/3768.htm"
                                       title="项目完成后的小总结" target="_blank">项目完成后的小总结</a>
                                    <span class="text-muted">xiaomiya</span>
<a class="tag" taget="_blank" href="/search/js/1.htm">js</a><a class="tag" taget="_blank" href="/search/%E6%80%BB%E7%BB%93/1.htm">总结</a><a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE/1.htm">项目</a>
                                    <div>项目完成了,突然想做个总结但是有点无从下手了。 
做之前对于客户端给的接口很模式。然而定义好了格式要求就如此的愉快了。 
先说说项目主要实现的功能吧 
1,按键精灵 
2,获取行情数据 
3,各种input输入条件判断 
4,发送数据(有json格式和string格式) 
5,获取预警条件列表和预警结果列表, 
6,排序, 
7,预警结果分页获取 
8,导出文件(excel,text等) 
9,修</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>