async関数を深く理解する——ES 6編
5410 ワード
意味と役割
関数を導入して,非同期動作がasyncにとってより便利になった.async関数とは、Generator関数のアスタリスク(内蔵アクチュエータ より良い意味 より広い適用性 戻り値はPromise です.
基本的な使い方
async関数はPromiseオブジェクトに戻ります.関数が実行されると、awaitと出会うと、非同期動作が完了するまで戻ってきます.その後、関数の内部にあるステートメントを実行します.
try...catch構造を使用して、複数回の試みを実現します.awaitの後のpromiseオブジェクトはエラーオブジェクトを投げて、catch方法のコールバック関数が割り当てられたエラーオブジェクトが呼び出しられます.
async関数の実現原理:Gennerator関数と自動執行器を一つの関数に包装します.
一例を通して、async関数とPromise、Generator関数の比較を見ました.
あるDOM要素の上に、一連のアニメーションが展開されていると仮定して、前のアニメーションが終了してから、次のアニメーションが開始されます.一つの動画が間違っていたら、もう次のアニメーションを実行しないで、前の成功したアニメーションの戻り値を返します.
Promiseの書き方
Generator関数の書き方
async関数の書き方:
1)手順に従って非同期操作を完了し、同時に実行する(時間の節約)
for await...of for...ofサイクルは同期のIteratorインターフェースを巡回するために使用され、新しく導入されたfor await...ofサイクルは、非同期のIteratorインターフェースを遍歴するために使用されます.
1)await命令の後のpromiseオブジェクトは、運転結果がrejectdかもしれないので、await命令をtry...catchコードブロックに入れたほうがいいです.
関数を導入して,非同期動作がasyncにとってより便利になった.async関数とは、Generator関数のアスタリスク(
*
)をasyncに置き換え、yield
をawait
に置き換えただけです.async関数のGenerator関数の改善は、以下の4点に反映されます.基本的な使い方
async関数はPromiseオブジェクトに戻ります.関数が実行されると、awaitと出会うと、非同期動作が完了するまで戻ってきます.その後、関数の内部にあるステートメントを実行します.
async function getStockPriceByName(name) {
const symbol = await getStockPriceByName(name);
const stockPrice = await getStockPriceByName(symbol);
return stockPrice;
}
getStockPriceByName("goog").then(function(result) {
console.log(result);
})
async関数から返ってきたpromiseオブジェクトは、内部のawait命令の後のpromiseオブジェクトが実行されるまで、ステータスが変化します.つまり、async関数内部の非同期操作オブジェクトが実行された後にのみ、thenメソッドで作成されたコールバック関数が実行されます.async function getTitle(url) {
let response = await fetch(url);
let html = await resoponse.text();
return html.match(/([\s\S] + ) /i)[i];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
通常、awaitコマンドの後にPromiseオブジェクトがあり、そのオブジェクトの結果に戻ります.promiseオブジェクトでない場合は、直接に対応する値awaitコマンドの後にthenableオブジェクト(すなわち、thenメソッドを定義する対象)を返すと、awaitはPromiseオブジェクトと同じです.class Sleep{
constructor(timeout) {
this.timout = timout;
}
then(resolve, reject) {
const startTime = Date.now();
setTimeout(
() => resolve(Date.now() - startTime),
this.timeout
);
}
}
(async () => {
const.actualTime = await new Sleep(1000);
console.log(actualTime);
})();
エラー処理try...catch構造を使用して、複数回の試みを実現します.awaitの後のpromiseオブジェクトはエラーオブジェクトを投げて、catch方法のコールバック関数が割り当てられたエラーオブジェクトが呼び出しられます.
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-throw-an-error');
break;
} catch (err) {
}
}
console.log(i);
}
test();
他の非同期処理方法との比較:async関数の実現原理:Gennerator関数と自動執行器を一つの関数に包装します.
一例を通して、async関数とPromise、Generator関数の比較を見ました.
あるDOM要素の上に、一連のアニメーションが展開されていると仮定して、前のアニメーションが終了してから、次のアニメーションが開始されます.一つの動画が間違っていたら、もう次のアニメーションを実行しないで、前の成功したアニメーションの戻り値を返します.
Promiseの書き方
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;
});
}
Promiseの書き方はコールバック関数の書き方よりかなり改善されていますが、一目で見て、 コードはまったくPromiseのAPI(then、catchなど)で、操作そのものの意味はかえって分かりにくいです.Generator関数の書き方
function chainAnimationsGenerator(elem, animations) {
return spawn(function*() {
let ret = null;
try{
for(let anim of animations) {
ret == yield anim(elem);
}
} catch(e) {
// ,
}
return ret;
});
}
上のコードはGenerator関数を使って各アニメーションを遍歴しています.意味はPromise書き方よりもはっきりしています.ユーザー定義の操作は全部spawn関数の内部にあります.この書き方の問題は、タスク実行器が必要で、自動的にGenerator関数を実行します.上のコードのspawn関数は自動実行器です.これはPromiseオブジェクトに戻ります.また、yield文の後の表現を保証しなければなりません.Promiseに戻らなければなりません.async関数の書き方:
async function chainAnimationsAsync(elem, animations){
let ret = null;
try {
for(let anim of animations) {
ret = await anim(elem);
}
} catch(e) {
// ,
}
return ret;
}
実例1)手順に従って非同期操作を完了し、同時に実行する(時間の節約)
async function logInOrder(urls) {
// URL
const textPromise = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
//
for (const textPromise of textPromise) {
console.log(await textPromise);
}
}
map法のパラメータはasync関数ですが、ASync関数の内部だけが転送されますので、同時に実行されます. 外部は影響を受けない.後のfor.ofサイクル内部はawaitを使用していますので、順番に出力することができます.for await...of for...ofサイクルは同期のIteratorインターフェースを巡回するために使用され、新しく導入されたfor await...ofサイクルは、非同期のIteratorインターフェースを遍歴するために使用されます.
async function f() {
for await (const x of createAsyncInterable(['a','b'])) {
console.log(x);
}
}
//a
//b
注意:1)await命令の後のpromiseオブジェクトは、運転結果がrejectdかもしれないので、await命令をtry...catchコードブロックに入れたほうがいいです.
async function myFunction() {
try {
await somethingThatReturnAPromise();
} catch(err) {
console.log(err);
}
}
//
async function myFunction() {
await somethingThatReturnAPromise()
.catch(function (err) {
console.log(err);
});
}
2)複数のawait命令の後の非同期操作は、もし継続関係が存在しないなら、それらを同時に出発させることが望ましい(推奨されない).let foo = await getFoo();
let bar = await getBar();
同時出発(推奨):プログラムの実行時間を短縮します.//
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
//
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
3)await命令はasync関数の中だけです.普通の関数ではエラーが発生します.async function dbFunc(db) {
let docs = [{},{},{}];
//
docs.forEach(function (doc) {
await db.post(doc);
});
}
// , await
function dbFunc(db) {// async
let docs = [{}, {}, {}];
//
docs.forEach(async function(doc) {
await db.post(doc);
});
}
// db.post , ,
// for
async function dbFunc(db) {
let docs = [{},{},{}];
for (let doc of docs) {
await db.post(doc);
}
}