JavaScript: Promise Chaining


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Giới thiệu về JavaScript Promise Chaining

Đôi khi, bạn muốn thực thi hai hoặc nhiều thao tác không đồng bộ có liên quan, trong đó thao tác tiếp theo bắt đầu bằng kết quả từ bước trước đó. Ví dụ:

Đầu tiên, tạo một Promise mới gọi resolve(10) sau 3 giây:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

Lưu ý rằng setTimeout() mô phỏng hoạt động không đồng bộ.

Sau đó, gọi phương thức then() của Promise:

p.then((result) => {
    console.log(result);
    return result * 2;
});

Hàm callback được truyền cho phương thức then() sẽ được thực thi sau khi Promise được giải quyết. Trong lệnh gọi lại, chúng tôi hiển thị kết quả của Promise và trả về một giá trị mới bằng 2 lần giá trị cũ (result*2).

Vì phương thức then() trả về một giá trị Promise mới có giá trị được giải quyết nên bạn có thể gọi phương thức then() khi trả về Promise như sau:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result);
    return result * 2;
}).then((result) => {
    console.log(result);
    return result * 3;
});

Output

10
20

Trong ví dụ này, giá trị trả về trong phương thức then() đầu tiên được truyền cho phương thức then() thứ hai. Bạn có thể tiếp tục gọi phương thức then() này một cách liên tiếp như sau:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result); // 10
    return result * 2;
}).then((result) => {
    console.log(result); // 20
    return result * 3;
}).then((result) => {
    console.log(result); // 60
    return result * 4;
});

Cách gọi các phương thức then() như thế này thường được gọi là promise chain.

Hình ảnh sau đây minh họa promise chain:

Nhiều handler cho một lời hứa

Khi bạn gọi phương thức then() này nhiều lần theo một Promise, đó không phải là promise chain. Ví dụ:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result); // 10
    return result * 2;
})

p.then((result) => {
    console.log(result); // 10
    return result * 3;
})

p.then((result) => {
    console.log(result); // 10
    return result * 4;
});

Trong ví dụ này, chúng tôi có nhiều handler cho một Promise. Những handler này không có mối quan hệ. Ngoài ra, chúng thực thi độc lập và không chuyển kết quả từ handler này sang handler khác như promise chain ở trên.

Hình ảnh sau đây minh họa một Promise có nhiều handler:

Trả về Promise

Khi bạn trả về một giá trị trong phương thức then(), phương thức then() đó sẽ trả về một giá trị Promise mới ngay lập tức được giải quyết với giá trị trả về đó. Ngoài ra, bạn có thể trả lại một Promise mới trong phương thức then() này, như sau:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(result * 2);
        }, 3 * 1000);
    });
}).then((result) => {
    console.log(result);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(result * 3);
        }, 3 * 1000);
    });
}).then(result => console.log(result));

Output

10
20
60

Ví dụ này hiển thị 10, 20 và 60 sau mỗi 3 giây. Đoạn mã này cho phép bạn thực hiện một số tác vụ theo trình tự.

Sau đây là đoạn mã đã sửa đổi ví dụ trên:

function generateNumber(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num);
    }, 3 * 1000);
  });
}

generateNumber(10)
  .then((result) => {
    console.log(result);
    return generateNumber(result * 2);
  })
  .then((result) => {
    console.log(result);
    return generateNumber(result * 3);
  })
  .then((result) => console.log(result));

Cú pháp Promise chain

Đôi khi, bạn có nhiều tác vụ không đồng bộ mà bạn muốn thực hiện theo trình tự. Ngoài ra, bạn cần chuyển kết quả của bước trước sang bước tiếp theo. Trong trường hợp này, bạn có thể sử dụng cú pháp sau:

step1()
    .then(result => step2(result))
    .then(result => step3(result))
    ...

Nếu bạn cần chuyển kết quả từ tác vụ trước sang tác vụ tiếp theo mà không chuyển kết quả, bạn sử dụng cú pháp sau:

step1()
    .then(step2)
    .then(step3)
    ...

Giả sử bạn muốn thực hiện tuần tự các thao tác không đồng bộ sau:

  • Đầu tiên, lấy người dùng từ cơ sở dữ liệu.
  • Thứ hai, nhận các dịch vụ của người dùng đã chọn.
  • Thứ ba, tính chi phí dịch vụ từ dịch vụ của người dùng.

Các hàm sau minh họa ba hoạt động không đồng bộ:

function getUser(userId) {
    return new Promise((resolve, reject) => {
        console.log('Get the user from the database.');
        setTimeout(() => {
            resolve({
                userId: userId,
                username: 'admin'
            });
        }, 1000);
    })
}

function getServices(user) {
    return new Promise((resolve, reject) => {
        console.log(`Get the services of ${user.username} from the API.`);
        setTimeout(() => {
            resolve(['Email', 'VPN', 'CDN']);
        }, 3 * 1000);
    });
}

function getServiceCost(services) {
    return new Promise((resolve, reject) => {
        console.log(`Calculate the service cost of ${services}.`);
        setTimeout(() => {
            resolve(services.length * 100);
        }, 2 * 1000);
    });
}

Phần sau đây sử dụng các promise chain để tuần tự hóa các chuỗi:

getUser(100)
    .then(getServices)
    .then(getServiceCost)
    .then(console.log);

Output

Get the user from the database.
Get the services of admin from the API.
Calculate the service cost of Email,VPN,CDN.
300

Lưu ý rằng ES2017 đã giới thiệu async/await giúp bạn viết mã đẹp hơn so với sử dụng kỹ thuật promise chain.

» Tiếp: JavaScript Promise.all()
« Trước: Phương thức apply()
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!