Node.js gốc Promise.all đang xử lý song song hay tuần tự?

192
Yanick Rochon 2015-06-14 11:20.

Tôi muốn làm rõ điểm này, vì tài liệu không quá rõ ràng về nó;

Q1: Được Promise.all(iterable)xử lý tất cả những lời hứa tuần tự hoặc song song? Hay cụ thể hơn, nó tương đương với việc chạy các lời hứa theo chuỗi như

p1.then(p2).then(p3).then(p4).then(p5)....

hoặc là nó một số loại khác của thuật toán mà tất cả p1, p2, p3, p4, p5, vv được gọi cùng một lúc (song song) và kết quả được trả về càng sớm càng tất cả quyết tâm (hoặc một Rejects)?

Câu hỏi 2: Nếu Promise.allchạy song song, có cách nào thuận tiện để chạy tuần tự lặp lại không?

Lưu ý : Tôi không muốn sử dụng Q hoặc Bluebird, nhưng tất cả các thông số kỹ thuật ES6 bản địa.

13 answers

279
Bergi 2015-06-14 11:28.

Promise.all(iterable)thực hiện mọi lời hứa không?

Không, lời hứa không thể "được thực hiện". Chúng bắt đầu tác vụ khi chúng đang được tạo - chúng chỉ đại diện cho kết quả - và bạn đang thực hiện mọi thứ song song ngay cả trước khi chuyển chúng đến Promise.all.

Promise.allchỉ chờ đợi nhiều lời hứa. Nó không quan tâm đến thứ tự chúng giải quyết, hoặc liệu các tính toán có chạy song song hay không.

có cách nào thuận tiện để chạy tuần tự có thể lặp lại không?

Nếu bạn đã có những lời hứa của mình, bạn không thể làm được gì nhiều ngoài việc Promise.all([p1, p2, p3, …])(không có khái niệm về trình tự). Nhưng nếu bạn có một hàm không đồng bộ có thể lặp lại, bạn thực sự có thể chạy chúng tuần tự. Về cơ bản, bạn cần nhận được từ

[fn1, fn2, fn3, …]

đến

fn1().then(fn2).then(fn3).then(…)

và giải pháp để làm điều đó là sử dụng Array::reduce:

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())
69
david_adler 2017-09-07 14:15.

Song song

await Promise.all(items.map(async item => { await fetchItem(item) }))

Ưu điểm: Nhanh hơn. Tất cả các lần lặp sẽ được bắt đầu ngay cả khi không thành công.

Theo thứ tự

for (let i = 0; i < items.length; i++) {
    await fetchItem(items[i])
}

Ưu điểm: Các biến trong vòng lặp có thể được chia sẻ theo từng lần lặp. Hoạt động giống như mã đồng bộ mệnh lệnh bình thường.

11
tkarls 2017-02-06 23:47.

Câu trả lời của Bergis đã giúp tôi đi đúng hướng khi sử dụng Array.reduce.

Tuy nhiên, để thực sự nhận được các hàm trả về các lời hứa của tôi để thực hiện lần lượt, tôi phải thêm một số lồng ghép nữa.

Trường hợp sử dụng thực sự của tôi là một mảng các tệp mà tôi cần chuyển theo thứ tự lần lượt do các giới hạn ở hạ lưu ...

Đây là những gì tôi đã kết thúc với.

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(() => {
            return transferFile(theFile); //function returns a promise
        });
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

Như các câu trả lời trước gợi ý, sử dụng:

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(transferFile(theFile));
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

đã không đợi quá trình chuyển hoàn tất trước khi bắt đầu một quá trình khác và văn bản "Tất cả các tệp đã được chuyển" cũng xuất hiện trước khi quá trình chuyển tệp đầu tiên được bắt đầu.

Tôi không chắc mình đã làm gì sai, nhưng muốn chia sẻ những gì có ích cho tôi.

Chỉnh sửa: Kể từ khi tôi viết bài đăng này, bây giờ tôi đã hiểu tại sao phiên bản đầu tiên không hoạt động. then () mong đợi một hàm trả về một lời hứa. Vì vậy, bạn nên chuyển vào tên hàm mà không có dấu ngoặc đơn! Bây giờ, hàm của tôi muốn một đối số, vì vậy tôi cần phải bọc trong một hàm ẩn danh không lấy đối số!

7
Adrien De Peretti 2020-01-04 13:12.

NodeJS không chạy các lời hứa song song, nó chạy chúng đồng thời vì nó là một kiến ​​trúc vòng lặp sự kiện một luồng. Có khả năng chạy mọi thứ song song bằng cách tạo một tiến trình con mới để tận dụng lợi thế của CPU đa lõi.

Parallel Vs Concurent

Trên thực tế, những gì Promise.allthực hiện là, xếp chồng hàm Promise vào hàng đợi thích hợp (xem kiến ​​trúc vòng lặp sự kiện) chạy chúng đồng thời (gọi P1, P2, ...) sau đó chờ từng kết quả, sau đó giải quyết Promise.all với tất cả các hứa hẹn các kết quả. Promise.all sẽ thất bại ở lần thất bại đầu tiên, trừ khi bạn đã tự mình quản lý việc từ chối.

Có một sự khác biệt lớn giữa song song và đồng thời, cái đầu tiên sẽ chạy tính toán khác nhau trong quá trình riêng biệt vào chính xác cùng một lúc và chúng sẽ tiến triển ở đó rythme, trong khi cái kia sẽ lần lượt thực hiện các phép tính khác nhau mà không cần chờ lần trước. tính toán để hoàn thành và tiến bộ cùng một lúc mà không phụ thuộc vào nhau.

Cuối cùng, để trả lời câu hỏi của bạn, Promise.allsẽ không thực hiện song song hoặc tuần tự mà đồng thời.

4
TimoSolo 2016-09-09 03:58.

chỉ để giải thích thêm về câu trả lời của @ Bergi (rất ngắn gọn, nhưng khó hiểu;)

Mã này sẽ chạy từng mục trong mảng và thêm 'sau đó chuỗi' tiếp theo vào cuối;

function eachorder(prev,order) {
        return prev.then(function() {
          return get_order(order)
            .then(check_order)
            .then(update_order);
        });
    }
orderArray.reduce(eachorder,Promise.resolve());

hy vọng điều đó có ý nghĩa.

4
Mark Meyer 2017-08-17 16:48.

Bạn cũng có thể xử lý tuần tự một tệp có thể lặp lại với một hàm không đồng bộ bằng cách sử dụng một hàm đệ quy. Ví dụ: đã cho một mảng ađể xử lý với hàm không đồng bộ someAsyncFunction():

var a = [1, 2, 3, 4, 5, 6]

function someAsyncFunction(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("someAsyncFunction: ", n)
      resolve(n)
    }, Math.random() * 1500)
  })
}

//You can run each array sequentially with: 

function sequential(arr, index = 0) {
  if (index >= arr.length) return Promise.resolve()
  return someAsyncFunction(arr[index])
    .then(r => {
      console.log("got value: ", r)
      return sequential(arr, index + 1)
    })
}

sequential(a).then(() => console.log("done"))

3
Ayan 2019-06-22 21:42.

Sử dụng async chờ đợi một mảng các lời hứa có thể dễ dàng được thực thi tuần tự:

let a = [promise1, promise2, promise3];

async function func() {
  for(let i=0; i<a.length; i++){
    await a[i]();
  }  
}

func();

Lưu ý: Trong cách triển khai ở trên, nếu một lời hứa bị từ chối, phần còn lại sẽ không được thực hiện. Nếu bạn muốn tất cả các lời hứa của mình được thực hiện, hãy bọc await a[i]();bên trong của bạntry catch

2
Chintan Rajpara 2020-01-29 20:17.

song song, tương đông

xem ví dụ này

const resolveAfterTimeout = async i => {
  return new Promise(resolve => {
    console.log("CALLED");
    setTimeout(() => {
      resolve("RESOLVED", i);
    }, 5000);
  });
};

const call = async () => {
  const res = await Promise.all([
    resolveAfterTimeout(1),
    resolveAfterTimeout(2),
    resolveAfterTimeout(3),
    resolveAfterTimeout(4),
    resolveAfterTimeout(5),
    resolveAfterTimeout(6)
  ]);
  console.log({ res });
};

call();

bằng cách chạy mã, nó sẽ điều khiển "CALLED" cho tất cả sáu lời hứa và khi chúng được giải quyết, nó sẽ điều khiển mỗi 6 phản hồi sau khi hết thời gian chờ cùng một lúc

1
Nithi 2019-05-11 09:44.

Câu trả lời của Bergi đã giúp tôi thực hiện cuộc gọi đồng bộ. Tôi đã thêm một ví dụ bên dưới, nơi chúng tôi gọi từng hàm sau khi hàm trước đó được gọi.

function func1 (param1) {
    console.log("function1 : " + param1);
}
function func2 () {
    console.log("function2");
}
function func3 (param2, param3) {
    console.log("function3 : " + param2 + ", " + param3);
}

function func4 (param4) {
    console.log("function4 : " + param4);
}
param4 = "Kate";

//adding 3 functions to array

a=[
    ()=>func1("Hi"),
    ()=>func2(),
    ()=>func3("Lindsay",param4)
  ];

//adding 4th function

a.push(()=>func4("dad"));

//below does func1().then(func2).then(func3).then(func4)

a.reduce((p, fn) => p.then(fn), Promise.resolve());
0
Deepak Sisodiya 2016-02-26 01:36.

Bạn có thể làm điều đó bằng vòng lặp for.

hàm async trả về lời hứa

async function createClient(client) {
    return await Client.create(client);
}

let clients = [client1, client2, client3];

nếu bạn viết mã sau thì ứng dụng khách được tạo song song

const createdClientsArray = yield Promise.all(clients.map((client) =>
    createClient(client);
));

sau đó tất cả các máy khách được tạo song song. nhưng nếu bạn muốn tạo ứng dụng khách một cách tuần tự thì bạn nên sử dụng vòng lặp for

const createdClientsArray = [];
for(let i = 0; i < clients.length; i++) {
    const createdClient = yield createClient(clients[i]);
    createdClientsArray.push(createdClient);
}

sau đó tất cả các máy khách được tạo tuần tự.

viết mã vui vẻ :)

0
Nick Kotenberg 2017-09-08 06:22.

Tôi đã sử dụng for để giải quyết các lời hứa tuần tự. Tôi không chắc liệu nó có giúp ích gì ở đây không nhưng đây là những gì tôi đang làm.

async function run() {
    for (let val of arr) {
        const res = await someQuery(val)
        console.log(val)
    }
}

run().then().catch()
0
cestmoi 2018-01-30 00:27.

điều này có thể trả lời một phần câu hỏi của bạn.

vâng, bạn có thể xâu chuỗi một mảng các hàm trả về lời hứa như sau ... (điều này sẽ chuyển kết quả của từng hàm sang hàm tiếp theo). tất nhiên bạn có thể chỉnh sửa nó để chuyển cùng một đối số (hoặc không có đối số) cho mỗi hàm.

function tester1(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a + 1);
    }, 1000);
  })
}

function tester2(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a * 5);
    }, 1000);
  })
}

function promise_chain(args, list, results) {

  return new Promise(function(done, errs) {
    var fn = list.shift();
    if (results === undefined) results = [];
    if (typeof fn === 'function') {
      fn(args).then(function(result) {
        results.push(result);
        console.log(result);
        promise_chain(result, list, results).then(done);
      }, errs);
    } else {
      done(results);
    }

  });

}

promise_chain(0, [tester1, tester2, tester1, tester2, tester2]).then(console.log.bind(console), console.error.bind(console));

0
Jay 2020-01-03 10:29.

Tôi tình cờ xem được trang này khi đang cố gắng giải quyết một vấn đề trong NodeJS: tập hợp lại các khối tệp. Về cơ bản: Tôi có một mảng tên tệp. Tôi cần nối tất cả các tệp đó, theo đúng thứ tự, để tạo một tệp lớn. Tôi phải làm điều này không đồng bộ.

Mô-đun 'fs' của Node cung cấp appendFileSync nhưng tôi không muốn chặn máy chủ trong quá trình hoạt động này. Tôi muốn sử dụng mô-đun fs.promises và tìm cách liên kết những thứ này lại với nhau. Các ví dụ trên trang này không hoàn toàn phù hợp với tôi vì tôi thực sự cần hai thao tác: fsPromises.read () để đọc trong tệp chunk và fsPromises.appendFile () để nối với tệp đích. Có lẽ nếu tôi giỏi hơn với javascript, tôi có thể làm cho các câu trả lời trước đó phù hợp với tôi. ;-)

Tôi tình cờ thấy điều này ... https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/ ... và tôi đã có thể hack cùng một giải pháp hoạt động.

TLDR:

/**
 * sequentially append a list of files into a specified destination file
 */
exports.append_files = function (destinationFile, arrayOfFilenames) {
    return arrayOfFilenames.reduce((previousPromise, currentFile) => {
        return previousPromise.then(() => {
            return fsPromises.readFile(currentFile).then(fileContents => {
                return fsPromises.appendFile(destinationFile, fileContents);
            });
        });
    }, Promise.resolve());
};

Và đây là một bài kiểm tra đơn vị hoa nhài cho nó:

const fsPromises = require('fs').promises;
const fsUtils = require( ... );
const TEMPDIR = 'temp';

describe("test append_files", function() {
    it('append_files should work', async function(done) {
        try {
            // setup: create some files
            await fsPromises.mkdir(TEMPDIR);
            await fsPromises.writeFile(path.join(TEMPDIR, '1'), 'one');
            await fsPromises.writeFile(path.join(TEMPDIR, '2'), 'two');
            await fsPromises.writeFile(path.join(TEMPDIR, '3'), 'three');
            await fsPromises.writeFile(path.join(TEMPDIR, '4'), 'four');
            await fsPromises.writeFile(path.join(TEMPDIR, '5'), 'five');

            const filenameArray = [];
            for (var i=1; i < 6; i++) {
                filenameArray.push(path.join(TEMPDIR, i.toString()));
            }

            const DESTFILE = path.join(TEMPDIR, 'final');
            await fsUtils.append_files(DESTFILE, filenameArray);

            // confirm "final" file exists    
            const fsStat = await fsPromises.stat(DESTFILE);
            expect(fsStat.isFile()).toBeTruthy();

            // confirm content of the "final" file
            const expectedContent = new Buffer('onetwothreefourfive', 'utf8');
            var fileContents = await fsPromises.readFile(DESTFILE);
            expect(fileContents).toEqual(expectedContent);

            done();
        }
        catch (err) {
            fail(err);
        }
        finally {
        }
    });
});

Tôi hi vọng nó giúp ích cho ai đó.

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

Một thương hiệu kế thừa khác có được cuộc sống mới khi những người chủ mới của Black Opal có được Hội chợ thời trang

Một thương hiệu kế thừa khác có được cuộc sống mới khi những người chủ mới của Black Opal có được Hội chợ thời trang

Desiree Rogers, trái, và Cheryl Mayberry McKissack Chuyển sang, Fenty Beauty và Pat McGrath, có một đế chế làm đẹp mới do phụ nữ làm chủ đang trỗi dậy. Sau thương vụ mua lại mỹ phẩm Black Opal vào tháng 9, đội ngũ quyền lực của Giám đốc điều hành công ty Desiree Rogers và Chủ tịch Cheryl Mayberry McKissack đã thông báo mua lại thương hiệu làm đẹp tiên phong Fashion Fair từ người sáng lập Johnson Publishing Company (JPC).

Ngoài việc trở nên vô nghĩa, các bên bộc lộ giới tính giờ đây đã trở nên chết người

Ngoài việc trở nên vô nghĩa, các bên bộc lộ giới tính giờ đây đã trở nên chết người

Đối với những người có quá nhiều thời gian, tiệc tiết lộ giới tính là một cách thú vị để không cần áp đặt những định kiến ​​hạn chế lên thai nhi trước một đối tượng bạn bè giả vờ quan tâm một cách lịch sự. Tuy nhiên, việc chỉ dựa vào những chiếc bánh nướng có màu nhân tạo để biểu thị một tiếng hoo-ha hoặc một đứa trẻ đi tè đã trở nên xa xỉ trong các bậc cha mẹ trên Instagram.

Kirstjen Nielsen đang cố gắng đổi mới bản thân với tư cách là một người phụ nữ 'nói ra sự thật cho sức mạnh'

Kirstjen Nielsen đang cố gắng đổi mới bản thân với tư cách là một người phụ nữ 'nói ra sự thật cho sức mạnh'

Hôm thứ Tư, cựu Bộ trưởng An ninh Nội địa Kirstjen Nielsen vì một lý do nào đó đã được đưa ra một diễn đàn để phát biểu tại Hội nghị thượng đỉnh Những người phụ nữ quyền lực nhất của Fortune. Và có thể đoán trước được, người phụ nữ giám sát các nỗ lực (đang diễn ra) của chính quyền Trump nhằm chia cắt các gia đình ở biên giới đã sử dụng thời gian của mình như một cơ hội để tự bảo vệ mình và tự nhận mình là người — và hãy hít thở sâu ở đây — “đã nói sự thật với quyền lực.

Toyota Racing Development sẽ cho ra mắt Supra 3000GT Concept, Massive Wing vào tháng tới

Toyota Racing Development sẽ cho ra mắt Supra 3000GT Concept, Massive Wing vào tháng tới

Vì thế hệ mới nhất của chiếc xe điều chỉnh được yêu thích trên thế giới hiện đã có mặt tại đây, nên chỉ có điều kiện là Toyota Racing Development sẽ đưa phiên bản Supra thế hệ thứ năm sửa đổi của riêng mình đến SEMA vào tháng tới. Toyota đã cho chúng tôi một gợi ý về những gì sẽ xảy ra trong tuần này: một chiếc Concept GR Supra 3000GT hiện đại.

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Vụ kiện, nêu tên một số học khu, lập luận rằng dự luật "Không nói đồng tính" được ban hành gần đây của Florida "có hiệu quả im lặng và xóa bỏ học sinh và gia đình LGBTQ +"

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Tôi ghét từ "tàu đắm". Mọi người cảm thấy thoải mái trong la bàn đạo đức của riêng mình, và khi làm như vậy, họ thấy mình vượt qua sự phán xét.

Language