Làm cách nào để gói các lệnh gọi hàm không đồng bộ thành một hàm đồng bộ trong Node.js hoặc Javascript?


122

Giả sử bạn duy trì một thư viện hiển thị một hàm getData. Người dùng của bạn gọi nó để lấy dữ liệu thực tế:
var output = getData();
Dữ liệu ẩn được lưu trong một tệp để bạn triển khai getDatabằng Node.js được tích hợp sẵn fs.readFileSync. Rõ ràng là cả hai getDatafs.readFileSyncđều là các chức năng đồng bộ. Một ngày nọ, bạn được yêu cầu chuyển nguồn dữ liệu cơ bản sang một repo chẳng hạn như MongoDB, chỉ có thể được truy cập không đồng bộ. Bạn cũng được yêu cầu tránh làm phiền người dùng của mình, getDatakhông thể thay đổi API để trả về đơn thuần là một lời hứa hoặc yêu cầu tham số gọi lại. Làm thế nào để bạn đáp ứng cả hai yêu cầu?

Hàm không đồng bộ sử dụng lời gọi lại / lời hứa là DNA của JavasSript và Node.js. Bất kỳ ứng dụng JS không tầm thường nào đều có thể thấm nhuần phong cách mã hóa này. Nhưng thực tế này có thể dễ dàng dẫn đến cái gọi là kim tự tháp gọi lại của sự diệt vong. Thậm chí tệ hơn, nếu bất kỳ mã nào trong bất kỳ trình gọi nào trong chuỗi cuộc gọi phụ thuộc vào kết quả của hàm không đồng bộ, thì những mã đó cũng phải được bao bọc trong hàm gọi lại, áp đặt một ràng buộc về kiểu mã hóa đối với người gọi. Đôi khi, tôi nhận thấy cần phải đóng gói một hàm không đồng bộ (thường được cung cấp trong thư viện của bên thứ 3) thành một hàm đồng bộ để tránh tính toán lại toàn cục. Tìm kiếm giải pháp về chủ đề này thường kết thúc với Node Fibershoặc các gói npm bắt nguồn từ nó. Nhưng Fibers chỉ không thể giải quyết được vấn đề mà tôi đang gặp phải. Ngay cả ví dụ được cung cấp bởi tác giả của Fibers cũng minh họa sự thiếu hụt:

...
Fiber(function() {
    console.log('wait... ' + new Date);
    sleep(1000);
    console.log('ok... ' + new Date);
}).run();
console.log('back in main');

Sản lượng thực tế:

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)

Nếu chức năng Fiber thực sự biến chế độ ngủ của chức năng không đồng bộ thành đồng bộ, đầu ra sẽ là:

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
back in main

Tôi đã tạo một ví dụ đơn giản khác trong JSFiddle và tìm kiếm mã để mang lại đầu ra mong đợi. Tôi sẽ chấp nhận một giải pháp chỉ hoạt động trong Node.js vì vậy bạn có thể thoải mái yêu cầu bất kỳ gói npm nào mặc dù không hoạt động trong JSFiddle.


2
Các hàm không đồng bộ không bao giờ có thể được thực hiện đồng bộ trong Node và ngay cả khi chúng có thể, bạn cũng không nên. Vấn đề là trong mô-đun fs, bạn có thể thấy các chức năng hoàn toàn riêng biệt để truy cập đồng bộ và không đồng bộ vào hệ thống tệp. Điều tốt nhất bạn có thể làm là che dấu sự xuất hiện của không đồng bộ bằng các lời hứa hoặc điều tra (trình tạo trong ES6). Để quản lý các kim tự tháp gọi lại, hãy đặt tên cho chúng thay vì xác định trong một lệnh gọi hàm và sử dụng một thứ gì đó như thư viện không đồng bộ.
qubyte

8
Đối với dandavis, async làm tăng chi tiết triển khai cho chuỗi cuộc gọi, đôi khi buộc phải tái cấu trúc toàn cầu. Điều này gây bất lợi và thậm chí là tai hại đối với một ứng dụng phức tạp mà việc mô-đun hóa và ngăn chặn là quan trọng.
abbr

4
"Kim tự tháp gọi lại của sự diệt vong" chỉ là đại diện của vấn đề. Promise có thể ẩn hoặc ngụy trang nó nhưng không thể giải quyết thách thức thực sự: Nếu trình gọi của một hàm không đồng bộ phụ thuộc vào kết quả của hàm không đồng bộ, nó phải sử dụng gọi lại và người gọi của nó cũng vậy, v.v. Đây là một ví dụ cổ điển về việc áp đặt các ràng buộc đối với người gọi đơn giản vì chi tiết thực hiện.
abbr

1
@abbr: Cảm ơn mô-đun deasync, mô tả vấn đề của bạn chính xác là những gì tôi đang tìm kiếm và không thể tìm thấy bất kỳ giải pháp khả thi nào. Tôi đã lộn xộn với máy phát điện và các tệp lặp, nhưng đã đi đến kết luận giống như bạn.
Kevin Jhangiani

2
Cần lưu ý rằng hầu như không bao giờ là một ý tưởng hay để buộc một chức năng không đồng bộ được đồng bộ hóa. Bạn hầu như luôn có một giải pháp tốt hơn để giữ nguyên tính không đồng bộ của hàm, trong khi vẫn đạt được hiệu quả tương tự (như sắp xếp theo trình tự, cài đặt biến, v.v.).
Madara's Ghost,

Câu trả lời:


104

deasync biến chức năng không đồng bộ thành đồng bộ, được thực hiện với cơ chế chặn bằng cách gọi vòng lặp sự kiện Node.js ở lớp JavaScript. Do đó, deasync chỉ chặn chạy mã tiếp theo mà không chặn toàn bộ chuỗi, cũng như không bắt buộc phải chờ đợi. Với mô-đun này, đây là câu trả lời cho thử thách jsFiddle:

function AnticipatedSyncFunction(){
  var ret;
  setTimeout(function(){
      ret = "hello";
  },3000);
  while(ret === undefined) {
    require('deasync').runLoopOnce();
  }
  return ret;    
}


var output = AnticipatedSyncFunction();
//expected: output=hello (after waiting for 3 sec)
console.log("output="+output);
//actual: output=hello (after waiting for 3 sec)

(tuyên bố từ chối trách nhiệm: Tôi là đồng tác giả của deasync. Mô-đun được tạo sau khi đăng câu hỏi này và không tìm thấy đề xuất nào khả thi.)


Có ai khác đã may mắn với điều này? Tôi không thể làm cho nó hoạt động.
newman

3
Tôi không thể làm cho nó hoạt động bình thường. bạn nên cải thiện tài liệu của mình cho mô-đun này, nếu bạn muốn nó được sử dụng nhiều hơn. Tôi nghi ngờ các tác giả biết chính xác các phân nhánh là gì để sử dụng mô-đun, và nếu có, họ chắc chắn không ghi lại chúng.
Alexander Mills

5
Cho đến nay, có một vấn đề đã được xác nhận được ghi lại trong trình theo dõi vấn đề github. Sự cố đã được khắc phục trong Node v0.12. Phần còn lại mà tôi biết chỉ là những suy đoán vô căn cứ không có giá trị ghi lại. Nếu bạn cho rằng sự cố của mình là do deasync gây ra, hãy đăng một kịch bản độc lập, trùng lặp và tôi sẽ xem xét.
abbr

Tôi đã cố gắng sử dụng nó và tôi nhận được một số cải tiến trong kịch bản của mình nhưng tôi vẫn chưa gặp may. Tôi đã sửa đổi mã như sau: function AnticipatedSyncFunction(){ var ret; setTimeout(function(){ var startdate = new Date() //console.log(startdate) ret = "hello" + startdate; },3000); while(ret === undefined) { require('deasync').runLoopOnce(); } return ret; } var output = AnticipatedSyncFunction(); var startdate = new Date() console.log(startdate) console.log("output="+output); và tôi mong đợi sẽ thấy 3 giây khác nhau trong đầu ra ngày!
Alex,

@abbr cái này có thể được duyệt và sử dụng mà không phụ thuộc vào nút không>
Gandhi

5

Ngoài ra còn có một mô-đun đồng bộ hóa npm. được sử dụng để đồng bộ hóa quá trình thực hiện truy vấn.

Khi bạn muốn chạy các truy vấn song song theo cách đồng bộ thì nút hạn chế làm điều đó vì nó không bao giờ chờ phản hồi. và mô-đun đồng bộ rất hoàn hảo cho loại giải pháp đó.

Mã mẫu

/*require sync module*/
var Sync = require('sync');
    app.get('/',function(req,res,next){
      story.find().exec(function(err,data){
        var sync_function_data = find_user.sync(null, {name: "sanjeev"});
          res.send({story:data,user:sync_function_data});
        });
    });


    /*****sync function defined here *******/
    function find_user(req_json, callback) {
        process.nextTick(function () {

            users.find(req_json,function (err,data)
            {
                if (!err) {
                    callback(null, data);
                } else {
                    callback(null, err);
                }
            });
        });
    }

liên kết tham khảo: https://www.npmjs.com/package/sync


4

Nếu chức năng Fiber thực sự biến chế độ ngủ của chức năng không đồng bộ thành đồng bộ

Đúng. Bên trong sợi quang, chức năng chờ trước khi ghi nhật ký ok. Các sợi không làm cho các hàm không đồng bộ trở nên đồng bộ, nhưng cho phép viết mã trông đồng bộ sử dụng các hàm không đồng bộ và sau đó sẽ chạy không đồng bộ bên trong a Fiber.

Đôi khi, tôi nhận thấy cần phải đóng gói một hàm không đồng bộ thành một hàm đồng bộ để tránh tính toán lại toàn cục lớn.

Bạn không thể. Không thể thực hiện đồng bộ mã không đồng bộ. Bạn sẽ cần dự đoán điều đó trong mã toàn cầu của mình và viết nó theo kiểu không đồng bộ ngay từ đầu. Cho dù bạn bọc mã toàn cầu trong một sợi, sử dụng các hứa hẹn, trình tạo hứa hẹn hoặc gọi lại đơn giản tùy thuộc vào sở thích của bạn.

Mục tiêu của tôi là giảm thiểu tác động đến người gọi khi phương pháp thu thập dữ liệu được thay đổi từ đồng bộ hóa thành không đồng bộ

Cả lời hứa và sợi đều có thể làm được điều đó.


1
đây là điều tồi tệ nhất mà bạn có thể gặp phải với Node.js: "mã trông đồng bộ sử dụng các hàm không đồng bộ và sau đó sẽ chạy không đồng bộ." nếu API của bạn làm điều đó, bạn sẽ hủy hoại cuộc sống. nếu nó không đồng bộ, nó sẽ yêu cầu gọi lại và thông báo lỗi nếu không cung cấp lệnh gọi lại. đó là cách tốt nhất để tạo API, trừ khi mục tiêu của bạn là lừa mọi người.
Alexander Mills

@AlexMills: Vâng, điều đó thực sự rất kinh khủng . Tuy nhiên, may mắn là đây không phải là điều mà một API có thể làm được. Một API không đồng bộ luôn cần phải chấp nhận một cuộc gọi lại / trả về một lời hứa / mong đợi được chạy bên trong một sợi - nó không hoạt động nếu không có. Afaik, các sợi chủ yếu được sử dụng trong các tập lệnh nhanh chóng không bẩn bị chặn và không có bất kỳ đồng thời nào, nhưng muốn sử dụng các API không đồng bộ; giống như trong nút đôi khi có những trường hợp bạn sử dụng các fsphương thức đồng bộ .
Bergi

2
Tôi thường thích nút. Đặc biệt nếu tôi có thể sử dụng typecript thay vì js thuần túy. Nhưng toàn bộ điều vô nghĩa không đồng bộ này tràn ngập mọi thứ bạn làm và thực sự lây nhiễm mọi chức năng trong chuỗi cuộc gọi ngay khi bạn quyết định thực hiện một lệnh gọi không đồng bộ duy nhất là điều tôi thực sự ... thực sự ghét. Async api giống như một căn bệnh truyền nhiễm, một cuộc gọi lây nhiễm sang toàn bộ cơ sở mã của bạn buộc bạn phải viết lại tất cả mã bạn có. Tôi thực sự không hiểu làm thế nào mọi người có thể tranh luận đây là một điều tốt .
Kris

@Kris Node sử dụng mô hình không đồng bộ cho các tác vụ IO vì nó nhanh và đơn giản. Bạn cũng có thể thực hiện nhiều việc một cách đồng bộ, nhưng việc chặn diễn ra chậm vì bạn không thể làm bất cứ điều gì đồng thời - trừ khi bạn bắt đầu các chuỗi, điều này khiến mọi thứ trở nên phức tạp.
Bergi

@Bergi Tôi đã đọc bản tuyên ngôn nên biết các lập luận. Nhưng việc thay đổi mã hiện tại của bạn thành không đồng bộ ngay khi bạn nhấn lệnh gọi api đầu tiên mà không có mã tương đương đồng bộ hóa không đơn giản. Mọi thứ đều bị hỏng và mọi dòng mã đều phải được xem xét kỹ lưỡng. Trừ khi mã của bạn không bình thường, tôi đảm bảo ... sẽ mất một lúc để chuyển đổi và làm cho nó hoạt động trở lại sau khi chuyển đổi toàn bộ thành thành ngữ không đồng bộ.
Kris

2

Bạn phải sử dụng những lời hứa:

const asyncOperation = () => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{resolve("hi")}, 3000)
    })
}

const asyncFunction = async () => {
    return await asyncOperation();
}

const topDog = () => {
    asyncFunction().then((res) => {
        console.log(res);
    });
}

Tôi thích các định nghĩa hàm mũi tên hơn. Nhưng bất kỳ chuỗi nào có dạng "() => {...}" cũng có thể được viết thành "function () {...}"

Vì vậy, topDog không phải là async mặc dù gọi một hàm async.

nhập mô tả hình ảnh ở đây

CHỈNH SỬA: Tôi nhận ra rất nhiều lần bạn cần bọc một chức năng không đồng bộ bên trong một chức năng đồng bộ hóa bên trong một bộ điều khiển. Đối với những tình huống đó, đây là một mẹo nhỏ:

const getDemSweetDataz = (req, res) => {
    (async () => {
        try{
            res.status(200).json(
                await asyncOperation()
            );
        }
        catch(e){
            res.status(500).json(serviceResponse); //or whatever
        }
    })() //So we defined and immediately called this async function.
}

Sử dụng điều này với các lệnh gọi lại, bạn có thể thực hiện một gói không sử dụng các lời hứa:

const asyncOperation = () => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{resolve("hi")}, 3000)
    })
}

const asyncFunction = async (callback) => {
    let res = await asyncOperation();
    callback(res);
}

const topDog = () => {
    let callback = (res) => {
        console.log(res);
    };

    (async () => {
        await asyncFunction(callback)
    })()
}

Bằng cách áp dụng thủ thuật này cho EventEmitter, bạn có thể nhận được kết quả tương tự. Xác định trình lắng nghe của EventEmitter nơi tôi đã xác định lệnh gọi lại và phát ra sự kiện mà tôi đã gọi lệnh gọi lại.


1

Tôi không thể tìm thấy một kịch bản không thể giải quyết bằng cách sử dụng các sợi nút. Ví dụ bạn đã cung cấp bằng cách sử dụng các sợi nút hoạt động như mong đợi. Điều quan trọng là chạy tất cả mã có liên quan bên trong một sợi quang, vì vậy bạn không cần phải bắt đầu một sợi mới ở các vị trí ngẫu nhiên.

Hãy xem ví dụ: Giả sử bạn sử dụng một số khuôn khổ, đó là điểm đầu vào của ứng dụng của bạn (bạn không thể sửa đổi khuôn khổ này). Khung công tác này tải các mô-đun nodejs dưới dạng plugin và gọi một số phương thức trên plugin. Cho phép nói rằng khuôn khổ này chỉ chấp nhận các chức năng đồng bộ và không sử dụng các sợi của chính nó.

Có một thư viện mà bạn muốn sử dụng trong một trong các plugin của mình, nhưng thư viện này không đồng bộ và bạn cũng không muốn sửa đổi nó.

Không thể mang lại luồng chính khi không có sợi nào đang chạy, nhưng bạn vẫn có thể tạo các plugin bằng cách sử dụng sợi! Chỉ cần tạo một mục trình bao bọc để khởi động toàn bộ khung bên trong một sợi, vì vậy bạn có thể mang lại hiệu suất thực thi từ các plugin.

Nhược điểm: Nếu khung sử dụng setTimeouthoặc sử dụng Promisenội bộ, thì nó sẽ thoát khỏi bối cảnh sợi. Điều này có thể được giải quyết bằng cách chế giễu setTimeout, Promise.thenvà tất cả các xử lý sự kiện.

Vì vậy, đây là cách bạn có thể tạo ra một chất xơ cho đến khi a Promiseđược giải quyết. Mã này nhận một hàm không đồng bộ (Promise return) và tiếp tục lại sợi quang khi lời hứa được giải quyết:

framework-entry.js

console.log(require("./my-plugin").run());

async-lib.js

exports.getValueAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("Async Value");
    }, 100);
  });
};

my-plugin.js

const Fiber = require("fibers");

function fiberWaitFor(promiseOrValue) {
  var fiber = Fiber.current, error, value;
  Promise.resolve(promiseOrValue).then(v => {
    error = false;
    value = v;
    fiber.run();
  }, e => {
    error = true;
    value = e;
    fiber.run();
  });
  Fiber.yield();
  if (error) {
    throw value;
  } else {
    return value;
  }
}

const asyncLib = require("./async-lib");

exports.run = () => {
  return fiberWaitFor(asyncLib.getValueAsync());
};

my-entry.js

require("fibers")(() => {
  require("./framework-entry");
}).run();

Khi bạn chạy node framework-entry.jsnó sẽ ném một lỗi: Error: yield() called with no fiber running. Nếu bạn chạy node my-entry.jsnó hoạt động như mong đợi.


0

Thực hiện đồng bộ mã Node.js là điều cần thiết trong một số khía cạnh như cơ sở dữ liệu. Nhưng lợi thế thực tế của Node.js nằm ở mã không đồng bộ. Vì nó là một luồng không chặn.

chúng ta có thể đồng bộ hóa nó bằng cách sử dụng chức năng quan trọng Fiber () Sử dụng await () và defer (), chúng ta gọi tất cả các phương thức bằng await (). sau đó thay thế các hàm gọi lại bằng defer ().

Mã Async bình thường. Mã này sử dụng các chức năng CallBack.

function add (var a, var b, function(err,res){
       console.log(res);
});

 function sub (var res2, var b, function(err,res1){
           console.log(res);
    });

 function div (var res2, var b, function(err,res3){
           console.log(res3);
    });

Đồng bộ mã trên bằng cách sử dụng Fiber (), await () và defer ()

fiber(function(){
     var obj1 = await(function add(var a, var b,defer()));
     var obj2 = await(function sub(var obj1, var b, defer()));
     var obj3 = await(function sub(var obj2, var b, defer()));

});

Hy vọng điều này có thể giúp cho bạn. Cảm ơn bạn


0

Ngày nay, mẫu máy phát điện này có thể là một giải pháp trong nhiều tình huống.

Dưới đây là một ví dụ về lời nhắc bảng điều khiển tuần tự trong nodejs sử dụng hàm async readline.question:

var main = (function* () {

  // just import and initialize 'readline' in nodejs
  var r = require('readline')
  var rl = r.createInterface({input: process.stdin, output: process.stdout })

  // magic here, the callback is the iterator.next
  var answerA = yield rl.question('do you want this? ', r=>main.next(r))    

  // and again, in a sync fashion
  var answerB = yield rl.question('are you sure? ', r=>main.next(r))        

  // readline boilerplate
  rl.close()

  console.log(answerA, answerB)

})()  // <-- executed: iterator created from generator
main.next()     // kick off the iterator, 
                // runs until the first 'yield', including rightmost code
                // and waits until another main.next() happens

-1

Bạn không nên nhìn vào những gì xảy ra xung quanh cuộc gọi tạo ra sợi mà hãy xem những gì xảy ra bên trong sợi. Khi bạn đã ở bên trong sợi quang, bạn có thể lập trình theo kiểu đồng bộ. Ví dụ:

hàm f1 () {
    console.log ('chờ ...' + ngày mới);
    ngủ (1000);
    console.log ('ok ...' + ngày mới);   
}

hàm f2 () {
    f1 ();
    f1 ();
}

Sợi (hàm () {
    f2 ();
}).chạy();

Bên trong sợi mà bạn gọi f1, f2sleepnhư thể chúng được đồng bộ hóa.

Trong một ứng dụng web điển hình, bạn sẽ tạo Fiber trong bộ điều phối yêu cầu HTTP của mình. Sau khi hoàn thành, bạn có thể viết tất cả logic xử lý yêu cầu của mình theo kiểu đồng bộ, ngay cả khi nó gọi các hàm không đồng bộ (fs, cơ sở dữ liệu, v.v.).


Cảm ơn Bruno. Nhưng điều gì sẽ xảy ra nếu tôi cần kiểu đồng bộ hóa trong mã bootstrap cần được thực thi trước khi máy chủ liên kết với cổng tcp - chẳng hạn như cấu hình hoặc dữ liệu phải được đọc từ db được mở không đồng bộ? Tôi có thể kết thúc với việc gói toàn bộ server.js trong Fiber và tôi nghi ngờ điều đó sẽ giết chết sự đồng thời ở toàn bộ cấp quy trình. Tuy nhiên, đó là một gợi ý đáng để xác minh. Đối với tôi, giải pháp lý tưởng phải có thể bao hàm một hàm không đồng bộ để cung cấp cú pháp cuộc gọi đồng bộ và chỉ chặn các dòng mã tiếp theo trong chuỗi người gọi mà không phải hy sinh tính đồng thời ở cấp quá trình.
abbr

Bạn có thể bọc toàn bộ mã bootstrap của mình bên trong một lệnh gọi Fiber lớn. Đồng thời không phải là một vấn đề vì mã bootstrap thường cần chạy đến khi hoàn thành trước khi bạn bắt đầu cung cấp các yêu cầu. Ngoài ra, một sợi không ngăn cản các sợi khác chạy: mỗi khi bạn đạt được hiệu suất, bạn cho các sợi khác (và sợi chính) có cơ hội chạy.
Bruno Jouhier

Tôi đã gói tệp tin bootstrap Express server.js bằng sợi quang. Trình tự thực thi là những gì tôi đang tìm kiếm, nhưng gói đó không có bất kỳ ảnh hưởng nào đến trình xử lý yêu cầu. Vì vậy, tôi đoán phải áp dụng cùng một trình bao bọc cho MỖI người điều phối. Tôi đã từ bỏ vào thời điểm này vì nó dường như không làm được gì tốt hơn để giúp tránh tính lại toàn cầu. Mục tiêu của tôi là giảm thiểu tác động đến người gọi khi phương thức thu thập dữ liệu được thay đổi từ đồng bộ thành không đồng bộ trong lớp DAO và Sợi quang vẫn còn một chút thách thức.
abbr

@fred: Không có ý nghĩa nhiều khi "đồng bộ hóa" các luồng sự kiện như trình xử lý yêu cầu - bạn cần phải có một while(true) handleNextRequest()vòng lặp. Bao bọc mỗi trình xử lý yêu cầu trong một sợi sẽ.
Bergi

@fred: fiber sẽ không giúp bạn nhiều với Express vì lệnh gọi lại của Express không phải là lệnh gọi lại tiếp tục (lệnh gọi lại luôn được gọi chính xác một lần, có lỗi hoặc có kết quả). Nhưng các sợi sẽ giải quyết được kim tự tháp của sự diệt vong khi bạn có nhiều mã được viết trên đầu các API không đồng bộ với các lệnh gọi lại tiếp tục (như fs, mongodb và rất nhiều mã khác).
Bruno Jouhier

-2

Lúc đầu, tôi đã vật lộn với điều này với node.js và async.js là thư viện tốt nhất mà tôi tìm thấy để giúp bạn giải quyết vấn đề này. Nếu bạn muốn viết mã đồng bộ với nút, cách tiếp cận là theo cách này.

var async = require('async');

console.log('in main');

doABunchOfThings(function() {
  console.log('back in main');
});

function doABunchOfThings(fnCallback) {
  async.series([
    function(callback) {
      console.log('step 1');
      callback();
    },
    function(callback) {
      setTimeout(callback, 1000);
    },
    function(callback) {
      console.log('step 2');
      callback();
    },
    function(callback) {
      setTimeout(callback, 2000);
    },
    function(callback) {
      console.log('step 3');
      callback();
    },
  ], function(err, results) {
    console.log('done with things');
    fnCallback();
  });
}

chương trình này LUÔN sẽ tạo ra những điều sau đây ...

in main
step 1
step 2
step 3
done with things
back in main

2
asynchoạt động trong ví dụ của bạn b / c nó main, mà không quan tâm đến người gọi. Hãy tưởng tượng tất cả mã của bạn được bao bọc trong một hàm được cho là trả về kết quả của một trong các lệnh gọi hàm không đồng bộ của bạn. Nó có thể dễ dàng được chứng minh là không hoạt động bằng cách thêm console.log('return');vào cuối mã của bạn. Trong trường hợp này, đầu ra của returnsẽ xảy ra sau in mainnhưng trước đó step 1.
abbr

-11

Javascript là một ngôn ngữ luồng đơn, bạn không muốn chặn toàn bộ máy chủ của mình! Mã không đồng bộ loại bỏ, chạy đua các điều kiện bằng cách làm cho các phụ thuộc rõ ràng.

Học cách yêu thích mã không đồng bộ!

Hãy xem promisesmã không đồng bộ mà không tạo một kim tự tháp của địa ngục gọi lại. Tôi đề xuất thư viện PromQ cho node.js

httpGet(url.parse("http://example.org/")).then(function (res) {
    console.log(res.statusCode);  // maybe 302
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);  // maybe 200
});

http://howtonode.org/promises

CHỈNH SỬA: đây là câu trả lời gây tranh cãi nhất của tôi, nút hiện có từ khóa lợi nhuận, cho phép bạn xử lý mã không đồng bộ như thể nó là đồng bộ. http://blog.alexmaccaw.com/how-yield-will-transform-node


1
Promise chỉ diễn đạt lại một tham số gọi lại thay vì biến hàm thành đồng bộ.
abbr

2
bạn không muốn nó được đồng bộ hóa hoặc toàn bộ máy chủ của bạn sẽ chặn! stackoverflow.com/questions/17959663/…
roo2

1
Điều mong muốn là một cuộc gọi đồng bộ mà không chặn các sự kiện khác chẳng hạn như một yêu cầu khác đang được xử lý bởi Node.js. Theo định nghĩa, một hàm Sync chỉ có nghĩa là nó sẽ không quay trở lại trình gọi cho đến khi kết quả được tạo ra (không chỉ là một lời hứa). Nó không loại trừ trước máy chủ xử lý các sự kiện khác trong khi cuộc gọi bị chặn.
abbr

@fred: Tôi nghĩ bạn đang thiếu điểm hứa hẹn . Chúng không chỉ đơn giản là một mô hình trừu tượng của người quan sát, mà chúng cung cấp một cách để xâu chuỗi và soạn các hành động không đồng bộ.
Bergi

1
@Bergi, tôi sử dụng lời hứa rất nhiều và biết chính xác nó làm gì. Hiệu quả tất cả những gì nó đạt được là chia nhỏ một lệnh gọi hàm không đồng bộ thành nhiều lệnh / lệnh gọi. Nhưng nó không thay đổi kết quả - khi người gọi trả về, nó không thể trả về kết quả của hàm async. Kiểm tra ví dụ tôi đã đăng trong JSFiddle. Người gọi trong trường hợp đó là hàm AnticipatedSyncFunction và hàm không đồng bộ là setTimeout. Nếu bạn có thể trả lời thách thức của tôi bằng cách sử dụng lời hứa, vui lòng chỉ cho tôi.
abbr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.