编写异步程序的最高境界就是它看起来是同步的。
Async Call
以下枚举最为经典的用法:
series(tasks, [callback])串行执行waterfall(tasks, [callback])带参数传递的串行执行
parallel(tasks, [callback])并行执行whilst(test, fn, callback)Repeatedly call fn, while test returns true. Calls callback when stopped, or an error occurs.
Deferred / Promise
// 经典的针对 Promise 用途的考察doSomething().then(function () {return doSomethingElse();});doSomethin().then(function () {doSomethingElse();});doSomething().then(doSomethingElse());doSomething().then(doSomethingElse);
- 串行式异步处理:
- 避免:Callbacks-Hell 与 意大利面条式的代码
// 比较合理的串行 Promise 方法remotedb.allDocs(...).then(functioin (resultofAllDocs) {return localdb.put(...);}).then(function (resultOfPut) {return localdb.get(...);}).then(function (resultOfGet) {return localdb.put(...);}).catch(function (err) {console.log(err);});
- 并行式异步处理:
Promise.all()与Deferred.when([...]) - 摒弃
Deferred的思想- 使用 Promise 包装
new Promise(function (resolve, reject) {fs.readFile('myfile.txt', function (err, file) {if (err) {return reject(err);}resolve(file);});}).then(...)
catch()不可忽略.catchis specified for built-in Javascript promises and is “sugar” for.then(null, function(){}).
- 显示调用
return方便理解代码 - 同步和异步的模型
- 同步解析:
Promise.resolve(someSynchronousValue).then(...);
- 同步解析:
getUserByName('nolan').then(function (user) {if (user.isLoggedOut()) {throw new Error('user logged out!'); // throwing a synchronous error!}if (inMemoryCache[user.id]) {return inMemoryCache[user.id]; // returning a synchronous value!}return getUserAccountById(user.id); // returning a promise!}).then(function (userAccount) {// I got a user account!}).catch(function (err) {// Boo, I got an error!});
- 杜绝 Promise 穿透
// Callback HellgetArticleList(function(articles){getArticle(articles[0].id, function(article){getAuthor(article.authorId, function(author){alert(author.email);})})})function getAuthor(id, callback){$.ajax("http://beta.json-generator.com/api/json/get/E105pDLh",{author: id}).done(function(result){callback(result);})}function getArticle(id, callback){$.ajax("http://beta.json-generator.com/api/json/get/EkI02vUn",{id: id}).done(function(result){callback(result);})}function getArticleList(callback){$.ajax("http://beta.json-generator.com/api/json/get/Ey8JqwIh").done(function(result){callback(result);});}// PromisegetArticleList().then(articles => getArticle(articles[0].id)).then(article => getAuthor(article.authorId)).then(author => console.log(author.email));function getAuthor(id){return new Promise(function(resolve, reject){$.ajax("http://beta.json-generator.com/api/json/get/E105pDLh",{author: id}).done(function(result){resolve(result);})});}function getArticle(id){return new Promise(function(resolve, reject){$.ajax("http://beta.json-generator.com/api/json/get/EkI02vUn",{id: id}).done(function(result){resolve(result);})});}function getArticleList(){return new Promise(function(resolve, reject){$.ajax("http://beta.json-generator.com/api/json/get/Ey8JqwIh").done(function(result){resolve(result);});});}// Generatorfunction* run(){var articles = yield getArticleList();var article = yield getArticle(articles[0].id);var author = yield getAuthor(article.authorId);alert(author.email);}function runGenerator(){var gen = run();function go(result){if(result.done) return;result.value.then(function(r){go(gen.next(r));});}go(gen.next());}runGenerator();// Asyncasync function run() {var articles = await getArticleList();var article = await getArticle(articles[0].id);var author = await getAuthor(article.authorId);alert(author.email);}
Async / Await
More Elegant Usage of Async, it is much better than just promise.
async function logInOrder(urls) {
// fetch all the URLs in parallel
const textPromises = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
// log them in sequence
for (const textPromise of textPromises) {
console.log(await textPromise);
}
}
Processing a Mongo DB Cursor
const mongodb = require('mongodb');
test();
async function test() {
const db = await mongodb.MongoClient.connect('mongodb://localhost:27017/test');
await db.collection('Movies').drop();
await db.collection('Movies').insertMany([
{ name: 'Enter the Dragon' },
{ name: 'Ip Man' },
{ name: 'Kickboxer' }
]);
// Don't `await`, instead get a cursor
const cursor = db.collection('Movies').find();
// Use `next()` and `await` to exhaust the cursor
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
console.log(doc.name);
}
}
Process Multitasks
const bcrypt = require('bcrypt');
const NUM_SALT_ROUNDS = 8;
test();
async function test() {
const pws = ['password', 'password1', 'passw0rd'];
// `promises` is an array of promises, because `bcrypt.hash()` returns a
// promise if no callback is supplied.
const promises = pws.map(pw => bcrypt.hash(pw, NUM_SALT_ROUNDS));
/**
* Prints hashed passwords, for example:
* [ '$2a$08$nUmCaLsQ9rUaGHIiQgFpAOkE2QPrn1Pyx02s4s8HC2zlh7E.o9wxC',
* '$2a$08$wdktZmCtsGrorU1mFWvJIOx3A0fbT7yJktRsRfNXa9HLGHOZ8GRjS',
* '$2a$08$VCdMy8NSwC8r9ip8eKI1QuBd9wSxPnZoZBw8b1QskK77tL2gxrUk.' ]
*/
console.log(await Promise.all(promises));
}
Ref
- 参考文章 EFE
- Promise 你真正理解了么?
- Promise A+
- Async Lib
- Google Async Function Introduction
- Async / Await pattern in Node.js
