我最近一直在写js。在这期间,我在群里提的最多的问题就是
函数a在函数b执行之前就执行了,我应该怎么办?
我有一个用python写的网易云音乐的下载器,我最近在试图把它用nodejs重写一遍。
它的逻辑非常简单。但是我写了一半就写不下去了,因为一层一层的回调嵌套,代码已经成了>
形。
js一个重大的特点就是异步非阻塞,但是在一些情况下,下一步的操作需要依赖上一步的执行结果。这样就会有回调中再回调的情况出现。 当业务逻辑一复杂,回调的嵌套越来越多,可读性就会变差,维护起来也会很困难,这就是回调地狱。
node有很多第三方的模块用来将异步调用同步化,来解决这个问题。
回调函数
request.get(
'http://www.google.com/',
function (error,response,body){
console.log(JSON.response.body);
});
回调函数简单、容易理解,但是一层层嵌套下来,就形成了回调地狱,不利于代码的阅读。各个部分之间高度耦合,很难维护。
Promise
var promise = new Promise(function(resolve,reject){
request.get(
'http://www.google.com/',
function (error,response,body){
if(error)
reject(error);
resolve(JSON.response.body);
}
});
promise.then(function(data){
console.log(data);
});
用resolve和reject抛出返回值,将回调链式化。一方面使代码更优雅,另一方面也使回调更可控。
async/await
function getList() {
return new Promise(function(resolve, reject) {
request.get(
'http://www.example.cn/get_list',
function(error, response, body) {
resolve(JSON.parse(body));
}
)
});
}
async function main() {
try {
let list = await getList();
console.log(list);
} catch (err) {
console.log(err);
}
}
main()
async/await本质上是基于generator和promise的语法糖。async
表示这个函数当中存在阻塞的操作,await
则表示这个函数会阻塞后面的代码。await
函数返回的结果是一个promise对象,由于await
函数的结果有可能是reject
,因此建议用try…catch包裹await
的函数。
使用上述模块,一方面为了消除回调地狱,另一方面用于真正需要同步的操作。但是不要滥用这些同步化模块。JS作为异步的语言,回调是很自然的模式。尽快转变思维,避免习惯性地用同步的思维去思考js。不然就会像用面向过程的思想来写c++一样。
很多时候,如果仅仅为了避免回调地狱,其实不需要async,generator或promise,只要对代码进行简单的封装即可。就能管理好相当多的回调场景。
不好的写法:
orm.connect("mysql://username:password@host/database", function(err, db) {
if (err) throw err;
var Person = db.define("person", SCHEMA);
db.sync(function(err) {
if (err) throw err;
Person.create({ id: 1, name: "John", surname: "Doe", age: 27 }, function(err) {
if (err) throw err;
Person.find({ surname: "Doe" }, function(err, people) {
if (err) throw err;
console.log("People found: %d", people.length);
console.log("First person: %s, age %d", people[0].fullName(), people[0].age);
people[0].age = 16;
people[0].save(function(err) {});
});
});
});
})
好的写法:
var db = orm.connect("creds", function (success, db) {
if (!success) {
console.log("Could not connect to database!");
return;
}
var Person = db.define("person", SCHEMA);
});
db.xxxxx();
使用babel支持es7
如果想使用es7的新特性(比如async),但是当前环境还不支持。
可以使用bable将较新标准的代码转换为当前环境可以执行的代码
安装babel
npm install -g babel-cli
配置babel
向package.json
添加下述依赖,并安装
"dependencies": {
"babel-plugin-syntax-async-functions": "^6.1.4",
"babel-plugin-transform-regenerator": "^6.1.4",
"babel-polyfill": "^6.1.4",
"babel-preset-es2015": "^6.1.4",
"request": "^2.65.0"
}
npm install
创建.babelrc
,添加下述内容
{
"presets": ["es2015"],
"plugins": ["syntax-async-functions","transform-regenerator"]
}
转码并运行
向js代码中添加下述内容
require("babel-polyfill");
运行
babel origin.js -o dest.js && node dest.js
其中origin.js
为转码前的es7的代码,dest.js
为转码后的可以在当前环境下运行的代码。
发表回复/Leave a Reply