
2018/02/01
posted in
JavaScript
#异步
2018/02/01
posted in
JavaScript
#异步
异步变成对于js来说非常重要,解决异步应用的方法也在不断更新,有人说终极目标就是使用同步的语法解决异步问题。下面我们就来看下异步编程的几种解决方案。
回调函数:把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
console.log(data);
});
回调函数的缺点是在多个回调函数嵌套的时候会横向发展,出现“回调地狱”(回调函数噩梦/callback hell),阅读起来也比较恼火
fs.readFile(fileA, function (err, data) {
fs.readFile(fileB, function (err, data) {
// ...
});
});
Promise解决回调的方式其实是纵向发展,虽然解决了回调地狱,但是一大堆then,语义不清楚。
var readFile = require('fs-readfile-promise');
readFile(fileA)
.then(function(data){
console.log(data.toString());
})
.then(function(){
return readFile(fileB);
})
.then(function(data){
console.log(data.toString());
})
.catch(function(err) {
console.log(err);
});
多个线程互相协作,完成异步任务:
function* gen(x){
var y = yield x + 2;
return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next(2) // { value: 2, done: true }
调用 Generator 函数,会返回一个内部指针(即遍历器 )g 。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。
调用指针 g 的 next 方法,会移动内部指针(即执行异步任务的第一段),每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。
Generator调用的时候不会自动执行(不自带执行器),必须通过执行next()方法来将内部指针移动到下一步。
使用try...catch在Generator 函数内部部署错误处理代码,用于捕获函数体外抛出的错误。
function* gen(x){
try {
var y = yield x + 2;
} catch (e){
console.log(e);
}
return y;
}
var g = gen(1);
g.next();
g.throw('出错了');
// 出错了
var fetch = require('node-fetch');
//Fetch 模块会返回一个 Promise 对象
function* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url);
console.log(result.bio);
}
//调用
var g = gen();
var result = g.next();
result.value.then(function(data){
return data.json();
}).then(function(data){
g.next(data);
});
由于Generator不自带执行器,所以出现了一些工具来自动执行Generator的next(),具体的可以参见:
async函数是基于Generator函数实现的,也就是说是Generator函数的语法糖。相比较与Generator,有如下特点:
async function getStockPriceByName(name) {
var symbol = await getStockSymbol(name);
var stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result){
console.log(result);
});
异步编程的最高境界,就是根本不用关心它是不是异步,很多人认为Async是终极解决方案。
我们可以和Generator对比下:
// Generator
var fs = require('fs');
var readFile = function (fileName){
return new Promise(function (resolve, reject){
fs.readFile(fileName, function(error, data){
if (error) reject(error);
resolve(data);
});
});
};
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
gen.next()
gen.next()
gen.next()
//async
var asyncReadFile = async function (){
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中或者使用catch接收错误。
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
// 另一种写法
async function myFunction() {
await somethingThatReturnsAPromise().catch(function (err){
console.log(err);
});
}