Promise 链

摘要:在本教程中,您将学习有关 JavaScript Promise 链模式的知识,该模式将 Promise 连接起来以按顺序执行异步操作。

JavaScript Promise 链的介绍

有时,您希望执行两个或多个相关的异步操作,其中下一个操作从上一个操作的结果开始。例如

首先,创建一个新的 Promise,它在 3 秒后解析为数字 10

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});Code language: JavaScript (javascript)

请注意,setTimeout() 函数模拟了异步操作。

然后,调用 Promise 的 then() 方法

p.then((result) => {
    console.log(result);
    return result * 2;
});Code language: JavaScript (javascript)

传递给 then() 方法的回调在 Promise 解析后执行。在回调中,我们显示 Promise 的结果并返回一个乘以 2 的新值 (result*2)。

因为 then() 方法返回一个解析为一个值的新的 Promise,所以您可以在返回的 Promise 上像这样调用 then() 方法

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;
});Code language: JavaScript (javascript)

输出

10
20

在此示例中,第一个 then() 方法中的返回值被传递给第二个 then() 方法。您可以像这样连续调用 then() 方法

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;
});
Code language: JavaScript (javascript)

输出

10
20
60

我们像这样调用 then() 方法的方式通常被称为 Promise 链

下图说明了 Promise 链

JavaScript Promise Chaining

一个 Promise 的多个处理程序

当您多次在 Promise 上调用 then() 方法时,这不是 Promise 链。例如

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;
});Code language: JavaScript (javascript)

输出

10
10
10

在此示例中,我们对一个 Promise 有多个处理程序。这些处理程序之间没有关系。此外,它们独立执行,并且不像上面的 Promise 链那样将结果从一个传递到另一个。

下图说明了一个拥有多个处理程序的 Promise

JavaScript Promise Chaining - multiple handlers

在实践中,您很少会对一个 Promise 使用多个处理程序。

返回一个 Promise

当您在 then() 方法中返回一个值时,then() 方法会返回一个立即解析为返回值的新的 Promise

此外,您可以在 then() 方法中返回一个新的 Promise,像这样

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));
Code language: JavaScript (javascript)

输出

10
20
60

此示例显示了每 3 秒后的 10、20 和 60。这种代码模式允许您按顺序执行一些任务。

以下修改了上面的示例

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));
Code language: JavaScript (javascript)

Promise 链语法

有时,您需要按顺序执行多个异步任务。此外,您需要将上一步的结果传递到下一步。在这种情况下,您可以使用以下语法

step1()
    .then(result => step2(result))
    .then(result => step3(result))
    ...
Code language: JavaScript (javascript)

如果您需要将上一步的结果传递到下一步而不传递结果,请使用此语法

step1()
    .then(step2)
    .then(step3)
    ...
Code language: CSS (css)

假设您希望按顺序执行以下异步操作

  • 首先,从数据库中获取用户。
  • 其次,获取所选用户的服务。
  • 第三,计算用户服务的服务成本。

以下函数说明了三个异步操作

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);
    });
}Code language: JavaScript (javascript)

以下使用 Promise 来序列化序列

getUser(100)
    .then(getServices)
    .then(getServiceCost)
    .then(console.log);Code language: CSS (css)

输出

Get the user from the database.
Get the services of admin from the API.
Calculate the service cost of Email,VPN,CDN.
300Code language: JavaScript (javascript)

请注意,ES2017 引入了 async/await,它可以帮助您编写比使用 Promise 链技术更简洁的代码。

在本教程中,您已经学习了 Promise 链,它可以按顺序执行多个异步任务。

测验

本教程对您有帮助吗?