conclusion
- 1. The execution of async functions is exactly the same as ordinary functions, as long as one line, without calling the next method
- 2, await the original type value will automatically become an immediate Resolved Promise object
- 3,
async
Function of the internalreturn
The value returned by the statement becomesthen
Method to call the functionparameter
- 4,
async
Function returnedPromise
Object,Have to wait until
internalAll await
The commandbehind
thePromise
objectafter
For the state change to occur,unless
encounterreturn
Statements orThrow an error
- 5. Any Promise object after an await statement becomes a reject state
async
The function breaks execution - 6, it is better to await the “await” command in the try… In the catch block
- 7, async operation after multiple await commands, if there is no await relation, better let them fire simultaneously,await promise.all ([getFoo(), getBar()]))
meaning
1.async
Function isGenerator
Function syntax sugar
async
The function is to takeGenerator
Function asterisk (*
) replaceasync
That will beyield
replaceawait
That’s all
Using the Generator function, read two files in turn:
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());
};
Copy the code
The function gen in the above code can be written as 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());
};
Copy the code
Async functions passAt four o 'clock
Improvements to the Generator function
(1) Built-in actuators
Async functions are executed just like normal functions, with only one line, without calling the next method or using the CO module
Generator functions must be executed by an executor, hence the CO module, while async functions have their own executor. In other words, async functions are executed exactly like normal functions, with only one line.
No need to call the next method or use the CO module
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 asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
//async function execution
asyncReadFile();
Copy the code
(2) Better semantics
Async and await are semantic clearer than asterisks and yield.
async
There are asynchronous operations in the function
await
Representation: The following expression needs to wait for the result
(3) wider applicability
By convention of the CO module, a yield command can only be followed by a Thunk function or a Promise object.
async
Function of theawait
After the command, it can bePromise
The objects andA value of the original type
Values, strings, and Booleans, but at this pointAutomatically becomes an immediate Resolved Promise object
)
await
The value of the original type is automatically converted to immediatelyresolved
thePromise
Object (Resolved (Original value itself)
)
(4) Return the Promise
async
The return value of the function isPromise
Object, this is more thanGenerator
The return value of the function isIterator
Objects are much more convenient. You can usethen
Method specifies what to do next
async
The function can be completely viewed asMultiple asynchronous operations
, packaged into aPromise
Object, andawait
The command is the insidethen
The commandThe syntactic sugar
Basic usage
1.async
functionreturn
aPromise
Object,can
makeBy then
methodsAdd a callback function
2, whenFunction performs
When, once encounteredawait
I’m going to go back first,Etc.
toAsynchronous completion
Again,Then perform
The statement following the function body
async function getStockPriceByName(name) {
const symbol = await getStockSymbol(name);
const stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result) {
console.log(result); / / is stockPrice
});
Copy the code
The async keyword before the above code function indicates that there are asynchronous operations inside the function. When this function is called, a Promise object is immediately returned, so you can use then to listen for the completion of the function.
function timeout(ms) {
return new Promise((resolve) = > {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world'.5000);// Output 'hello world' after 5s
// Change it up a bit...
async function asyncPrint(value, ms) {
await timeout(ms);
return value;
}
asyncPrint('hello world'.5000).then((result) = >{
console.log(result);
});// Output 'hello world' after 5s
Copy the code
Due to theasync
The function returns zeroPromise
Object that can be used asawait
Command parameters
So, the above example could also be written as follows:
// Change timeout to async as well
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);
Copy the code
Async functions can be used in many ways:
Function declaration
Functional expression
Object methods
The method of the Class
Arrow function
// Function declaration
async function foo() {}
// Function expression
const foo = async function () {};
// Object method
let obj = { async foo(){}}; obj.foo().then(...)// Class method
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 (...). ;// Arrow function
const foo = async() = > {};Copy the code
grammar
Return a Promise object
The async function returns a Promise object.
async
Function of the internalreturn
The value returned by the statement becomesthen
Method to call the functionparameter
async function f() {
return 'hello world';
}
Function f returns a value that is received by the then callback.
f().then(v= > console.log(v))
// "hello world"
Copy the code
async
The function throws an error internally, which will result in a returnPromise
Object toreject
State, where the thrown error object is received by the catch method callback
async function f() {
throw new Error('Wrong');
}
f().then(
v= > console.log(v),
e= > console.log(e,'e'))// Error: Error,'e'
Copy the code
The state change of the Promise object
1.async
Function returnedPromise
Object,Have to wait until
internalAll await
The commandbehind
thePromise
objectafter
For the state change to occur,unless
encounterreturn
Statements orThrow an error
2, onlyasync
The asynchronous operation inside the function is executed only after it has been executedthen
Method specifies the callback function
async function getTitle(url) {
let response = await fetch(url);
let html = await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i) [1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"
Copy the code
The getTitle function has three internal operations: fetch the page, retrieve the text, and match the page title. The console.log in the then method is executed only when all three operations are complete.
Await orders
1.await
The command is followed by onePromise
Object that returns the result of that object
2. If notPromise
Object directly returns the corresponding value
async function f() {
/ / is equivalent to
// return 123;
return await 123;
}
f().then(v= > console.log(v))
Copy the code
3. After the await command is a thenable object (The object that defines the then method
), thenawait
It’s going to be equal toPromise
object
class Sleep {
constructor(timeout) {
this.timeout = timeout;
}
then(resolve, reject) {
const startTime = Date.now();
setTimeout(
() = > resolve(Date.now() - startTime),
this.timeout ); }} (async() = > {const actualTime = await new Sleep(1000);
console.log(actualTime); }) ();Copy the code
In the code above, the await command is followed by an instance of the Sleep object. This instance is not a Promise object, but because the then method is defined, await will treat it as a Promise.
4. Await the Promise object if it becomes rejectreject
The parameter will becatch
Method received by the callback function
async function f() {
await Promise.reject('Wrong');
}
f()
.then(v= > console.log(v))
.catch(e= > console.log(e,'e'))
// Error,'e'
Copy the code
In this code, the await statement is preceded by no return, but the reject argument is still passed to the catch callback.
Here the effect is the same if return is preceded by await.
async function f() {
return await Promise.reject('Wrong');
}
f()
.then(v= > console.log(v))
.catch(e= > console.log(e,'e'))
// Error,'e'
Copy the code
5. Any Promise object after an await statement becomes a reject stateasync
The function breaks execution
async function f() {
await Promise.reject('Wrong');
await Promise.resolve('hello world'); // Will not be executed
}
Copy the code
6. Do not interrupt subsequent asynchronous operations even if the previous one fails
(1) the try… catch
We can put the first await in a try… Inside the catch structure, so that the second await is executed regardless of whether the asynchronous operation succeeds or not.
async function f() {
try {
await Promise.reject('Wrong');
} catch(e) {
}
return await Promise.resolve('hello world');
}
f()
.then(v= > console.log(v))
// hello world
Copy the code
(2) await the Promise followed by a catch method
async function f() {
await Promise.reject('Wrong')
.catch(e= > console.log(e));
return await Promise.resolve('hello world');
}
f()
.then(v= > console.log(v))
/ / make a mistake
// hello world
Copy the code
Error handling
1, ifawait
The following asynchronous operation error, then equal toasync
Function returnedPromise
The object isreject
sync function f() {
await new Promise(function (resolve, reject) {
throw new Error('Wrong');
});
}
f()
.then(v= > console.log(v))
.catch(e= > console.log(e,'e'))
// Error: Error,'e'
Copy the code
2. The way to prevent mistakes is to put it intry... catch
Inside the code block
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('Wrong');
});
} catch(e) {
}
return await('hello world');
}
Copy the code
3, if there are multipleawait
Commands can be placed uniformlytry... catch
In the structure
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); }}Copy the code
4, the use oftry... catch
Structure, implementationRepeated attempts
// If the await operation succeeds, a break statement is used to exit the loop; If it fails, it is caught by a catch statement and the next loop is passed.
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();
Copy the code
Use caution points
1, it is better to put await command in try… In the catch block
Await the Promise object after the await command, and the result may be rejected, so it is better to await the await command in the try… In the catch block.
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err); }}// Another way to write it
async function myFunction() {
await somethingThatReturnsAPromise()
.catch(function (err) {
console.log(err);
});
}
Copy the code
2, async operation after multiple await commands, if there is no await relation, better let them fire simultaneously,await promise.all ([getFoo(), getBar()]))
let foo = await getFoo();
let bar = await getBar();
Copy the code
GetFoo and getBar are two independent asynchronous (that is, non-dependent) operations written as a secondary relationship. This is time consuming because getBar will not be executed until getFoo is done, and they can be fired at the same time.
/ / write one
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
/ / write two
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
Copy the code
3. Await commands can only be used in async functions
If you use a normal function, you get an error.
async function dbFuc(db) {
let docs = [{}, {}, {}];
/ / an error
docs.forEach(function (doc) {
await db.post(doc);
});
}
Copy the code
If you haveforEach
The parameter of the method is changed toasync
Delta function, there’s also a problem
function dbFuc(db) { // Async is not required here
let docs = [{}, {}, {}];
// May get an error result
docs.forEach(async function (doc) {
await db.post(doc);
});
}
Copy the code
The above code may not work because the three db.POST operations will be executed concurrently, that is, at the same time, rather than secondarily.
The correct way to write it is to usefor
cycle
async function dbFuc(db) {
let docs = [{}, {}, {}];
for (let doc of docs) {
awaitdb.post(doc); }}Copy the code
Multiple requests are executed concurrently, using the promise.all method
When all three requests are resolved, these two options will have the same effect.
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) = > db.post(doc));
let results = await Promise.all(promises);
console.log(results);
}
// Or use the following notation
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);
}
Copy the code
esm
The module loader supports the top layerawait
, i.e.,await
The command may not be placedasync
In the function, just use
// async
const start = async() = > {const res = await fetch('google.com');
return res.text();
};
start().then(console.log);
// top-level await
const res = await fetch('google.com');
console.log(await res.text());
// The second script must use the ESM loader to take effect.
Copy the code
4. Async functions can preserve the run stack
const a = () = > {
b().then(() = > c());
};
Copy the code
In the code above:
- function
a
An asynchronous task runs internallyb()
. - when
b()
At run time, the functiona()
Execution continues without interruption. - Wait until the
b()
Run done, maybea()
It was over a long time ago,b()
The context is gone. - if
b()
orc()
Error, error stack willDo not include (a)
.
Change the above example to async
const a = async() = > {await b();
c();
};
Copy the code
In the code above, when b() runs, a() is paused, and the context is saved. If b() or c() reports an error, the error stack will include a().
The implementation principle of async function
async
And the way that functions are implemented is that the Generator
Functions andautoactuator
, wrapped in a function
async function fn(args) {
// ...
}
/ / is equivalent to
function fn(args) {
return spawn(function* () {
// ...
});
}
Copy the code
All async functions can be written in the second form above, where the spawn function is the auto-executor.
Here’s an implementation of the spawn function, which is basically a copy of the autoexecutor above.
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); });
});
}
Copy the code
Comparison with other asynchronous processing methods
Example: Suppose a series of animations are deployed on a DOM element, and the first animation ends before the next one begins. If one of the animations fails, it does not proceed further and returns the return value of the last successfully executed animation.
Written Promise
function chainAnimationsPromise(elem, animations) {
// The ret variable is used to hold the return value of the previous animation
let ret = null;
// Create an empty Promise
let p = Promise.resolve();
// Add all animations using the then method
for(let anim of animations) {
p = p.then(function(val) {
ret = val;
return anim(elem);
});
}
// Return a Promise with an error-catching mechanism deployed
return p.catch(function(e) {
/* Ignore the error and continue with */
}).then(function() {
return ret;
});
}
Copy the code
Writing of a Generator function
function chainAnimationsGenerator(elem, animations) {
return spawn(function* () {
let ret = null;
try {
for(let anim of animations) {
ret = yieldanim(elem); }}catch(e) {
/* Ignore the error and continue with */
}
return ret;
});
}
Copy the code
The problem with this is that you must have a task runner that automatically executes the Generator. The spawn function of the above code is the spawn function that returns a Promise object, and the expression following the yield statement must return a Promise.
Async function writing method
async function chainAnimationsAsync(elem, animations) {
let ret = null;
try {
for(let anim of animations) {
ret = awaitanim(elem); }}catch(e) {
/* Ignore the error and continue with */
}
return ret;
}
Copy the code
Example: Complete asynchronous operations sequentially
Read a set of urls remotely in turn and output the results in the order they were read.
Written Promise
function logInOrder(urls) {
// Read all urls remotely
const textPromises = urls.map(url= > {
return fetch(url).then(response= > response.text());
});
// Output in order
textPromises.reduce((chain, textPromise) = > {
return chain.then(() = > textPromise)
.then(text= > console.log(text));
}, Promise.resolve());
}
Copy the code
The code above uses the fetch method to read a set of urls remotely simultaneously. Each fetch operation returns a Promise object into the textPromises array. The Reduce method then processes each Promise object in turn, and then uses then to concatenate all the Promise objects so that the results can be output in turn.
The async function implements concurrency
Note: Onlyasync
The internal execution of a function is secondary, and the external execution is unaffected
async function logInOrder(urls) {
for (const url of urls) {
const response = await fetch(url);
console.log(awaitresponse.text()); }}Copy the code
The code problem is: all remote operations are secondary. Only when the previous URL returns a result will the next URL be read, which is inefficient and a waste of time.
What we need is to make remote requests concurrently.
Making remote requests concurrently: each async function is independent of each other (and the parent async function)
async function logInOrder(urls) {
// Read the remote URL concurrently
// Each async function is independent of each other (and not affected by the parent async function)
const textPromises = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
// Output in order
// Output in order with await in parent async function
for (const textPromise of textPromises) {
console.log(awaittextPromise); }}Copy the code
Although the map method takes an async function as an argument, it is executed concurrently because only the async function internally is executed secondary and the outside is unaffected. Behind the for… The of loop uses await inside, so it outputs sequentially.