Cách lập trình đồng bộ trên nodejs?



  • Cho em hỏi làm sao lập trình được đồng bộ trong Node.js vậy?
    Ví dụ:

    var request = require('request');

    for(var k=1; k<=10; k++){

    var url = 'http://www.24h.com.vn/'+k;
    request(url, function(err, response, body){
    console.log(k);
    })
    }
    Khi in ra kết quả thì là 10 số 11.
    Nhưng em muốn khi hàm callback thực hiện xong thì vòng lặp mới tiếp tục.
    Ai có thể giúp em được k ạ? em vừa mới học Nodejs :)



  • Em hãy sử dụng 1 thư viện đồng bộ thay thế thằng request như sync-request là được :D. Về cơ bản nên làm quen với bất đồng bộ vì nó là sức mạnh của nodejs mà :| . Ban đầu hơi khó dùng chút làm nhiều sẽ quen thôi . Với for loop để tránh side effect thì dùng let nhé thay vì dùng var :|



  • Cám ơn anh :)
    Em cũng đã dùng sync-request rùi :) Nhưng em cũng muốn một số hàm khác cũng đồng bộ nữa anh à :)
    Em biết bất đồng bộ là sức mạnh của nodejs cơ mà nếu gọi hàm callback nhiều quá thì có bị tràn bộ nhớ đệm k anh?
    Em tìm trên mạng thấy họ dùng async nhưng toàn hướng dẫn bằng tiếng anh nên e cũng k hiểu cho lắm :)
    Em thấy có 1 cách là như sau:
    var request = require('request');
    for(var k=1; k<=10; k++){
    var check;
    var url = 'http://www.24h.com.vn/'+k;
    request(url, function(err, response, body){
    console.log(k);
    check=1;
    });
    while(check === undefined){};
    }
    cơ mà nó k chạy anh à :) anh giải thích hộ e được k :)



  • Bạn thử cách này sễ in ra được 10 số nhưng không theo thứ tự nha.

    var request = require('request');

    for(var k=1; k<=10; k++){

    var url = 'http://www.24h.com.vn/'+k;
    !function(k) {
    request(url, function(err, response, body){
    console.log(k);
    });
    }(k);
    }



  • Cái này thì không phải lo tràn đâu em,

    Em có thể viết callback theo dạng này. Lần lượt add các callback vào nhau

    function next(i) {
        var url = 'http://www.24h.com.vn/' + i;
    
        if (i < 10) {
            request(url, function (err, response, body) {
                console.log(i);
                next(i + 1)
            });
        }
    }
    next(0)
    

    Để đơn giản hơn có thể dùng các thư viện có sẵn. ES7 có hỗ trợ async và await có thể giải quyết vấn đề này đơn giản hơn =)



  • @phucpnt Cám ơn bạn :) bạn giải thích code hộ mình được k? !function là sao vậy bạn?



  • @Quốc-Cường Vâng :) cám ơn anh :)



  • Trước tiên, bạn cần hiểu được nguyên nhân của tình trạng trên.

    Một biến được định nghĩa bằng từ khóa var sẽ tồn tại trong phạm vị một hàm (function scope). Do vậy, ở ví dụ của bạn, biến k sẽ bị ghi đè sau mỗi một vòng lặp. Mặt khác, request là một hàm không đồng bộ (async function) nên ở thời điểm hàm này được thực thi thì vòng lặp đã được thực thi xong và biến k có giá trị là 10 (giá trị của vòng lặp cuối cùng).

    Có 3 cách để giải quyết vấn đề:

    • Closure
      • Nhược điểm là code khó đọc và tốn bộ nhớ.
    for(var k=1; k<=10; k++){
        (function(i){
            var url = 'http://www.24h.com.vn/'+i;
            request(url, function(err, response, body){
                console.log(i);
            });
        })(k)
    }
    
    • Recursion (hồi quy)
      • Nhược điểm là chậm, do các hàm không đồng bộ, thay vì được thực thi song song, sẽ được thực thi có thứ tự.
    function next(i) {
        if (i > 11)  return;
        var url = 'http://www.24h.com.vn/' + i;
        request(url, function (err, response, body) {
            console.log(i);
            next(++i);
        });
    }
    next(1);
    
    • Định nghĩa biến bằng từ khóa let
      • Đây là giải pháp tốt nhất trong trường hợp này. Nhược điểm duy nhất của phương pháp này là từ khóa let hiện tại chỉ có sẵn ở strict mode. Từ khóa let sẽ định nghĩa một biến mà phạm vị tồn tại của nó chỉ trong một cặp block (block scope). Do đó biến sẽ không bị ghi đè qua các vòng lặp.
    //kích hoạt strict mode
    'use strict';
    for(let i=1; i<=10; i++){
            var url = 'http://www.24h.com.vn/'+i;
            request(url, function(err, response, body){
                console.log(i);
            });
    }


  • Vâng :) cám ơn anh :) em cũng thử sử dụng let nhưng mà nó báo lỗi strict mode :) nhờ ví dụ của anh mà e biết được cách sửa rùi :) cám ơn anh :)



  • Nên sử dụng thư viện async của caolan :)

    Lập trình đồng bộ sử dụng async.waterfall rất hiệu quả nhé bạn :)



  • Mọi người đã trả lời đầy đủ cả rồi. Mình chỉ có một recommend cho bạn là đọc Series You don't know JS , trong đó có một bộ Scope and Closures [1] giải thích rất kĩ từ việc compiler theory (không phải bạn viết chuơng trình như thế nào thì nó chạy top-down đâu nha) cho đến các vấn đề liên quan đến scope (function scope, lexical scope).

    [1]. https://github.com/getify/You-Dont-Know-JS/blob/master/scope & closures/README.md#you-dont-know-js-scope--closures

    Thân,



  • @tresdin đã nói:

    'use strict';
    for(let i=1; i<=10; i++){
    var url = 'http://www.24h.com.vn/'+i;
    request(url, function(err, response, body){
    console.log(i);
    });
    }

    Với các ví dụ trên thì chỉ có dùng recusion là log ra đúng thứ tự, còn lại sẽ theo thứ tự callback của request. Theo ý mình vậy có đúng không ?



  • bạn ơi cho mình hỏi nếu mình viết kết hợp giữa q và yield thì nó sẽ giống cách nào trong 3 cách của bạn vậy?

    Q.async(function *(){
    for(var i=1; i<=10; i++){
    var result = yield customRequest(i)
    console.log(result);
    }
    });

    function customRequest(i){
    var d = Q.defer();
    var url = 'http://www.24h.com.vn/'+i;
    request(url, function(err, response, body){
    d.resolve(i);
    });
    return d.promise;
    }


Log in to reply