编写异步程序的最高境界就是它看起来是同步的。
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()
不可忽略.catch
is 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 Hell
getArticleList(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);
});
}
// Promise
getArticleList()
.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);
});
});
}
// Generator
function* 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();
// Async
async 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