JavaScript代码同步化




我最近一直在写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为转码后的可以在当前环境下运行的代码。

参考资料

https://cnodejs.org/topic/5640b80d3a6aa72c5e0030b6http://aisk.me/using-async-await-to-avoid-callback-hell/http://stackoverflow.com/questions/10669561/handling-node-js-async-returns-with-require-node-ormhttp://masnun.com/2015/11/11/using-es7-asyncawait-today-with-babel.html




Posted

in

by

Comments

发表回复/Leave a Reply

您的电子邮箱地址不会被公开。/Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.