如何使用promise

git项目地址:https://github.com/WangShuangshuang/promise

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Promise</title>
</head>
<body>
<section>
    <div>Name: <span id="name"></span></div>
    <div>Age: <span id="age"></span></div>
    <div>Sex: <span id="sex"></span></div>
</section>
<script src="index.js"></script>
</body>
</html>

index.js:

/**
 * Created by on 2019/7/1.
 */
const demo = {
    getName: function(){
        return new Promise(function(resolve, reject){
                setTimeout(function(){
                    resolve('Lily1');
                }, 1000);
            })
    },
    getAge: function(){
        return new Promise(function(resolve, reject){
                setTimeout(function(){

                    // 执行此句将导致本promise rejected, 则Promise.all的.then()不会执行
                    // reject('error!!');

                    resolve('18');
                }, 2000);
            })
    },
    getSex: function(){
        return new Promise(function(resolve, reject){
                setTimeout(function(){

                    // 执行此句将导致本promise rejected, 则Promise.all的.then()不会执行
                    // reject('error!!!');

                    resolve('Lady');
                }, 3000);
            })
    },

    displayOneByOne: function(){
        console.log('0', new Date().getSeconds());
        demo.getName()
            .then(function(name){
                console.log('1', new Date().getSeconds())
                document.getElementById('name').innerHTML = name;
                demo.getAge().then(function(age){
                    console.log('2', new Date().getSeconds())
                    document.getElementById('age').innerHTML = age;
                    demo.getSex().then(function(sex){
                        console.log('3', new Date().getSeconds())
                        document.getElementById('sex').innerHTML = sex;
                        console.log('finished.')
                    })
                })
             })
    },
    displayOnTheSameTime: function(){
        console.log('0', new Date().getSeconds());
        Promise.all([demo.getName(), demo.getAge(), demo.getSex()])
            .then(function(resArray){
                console.log('resArray', resArray);
                console.log('1', new Date().getSeconds());
                document.getElementById('name').innerHTML = resArray[0];
                document.getElementById('age').innerHTML = resArray[1];
                document.getElementById('sex').innerHTML = resArray[2];
            })
            .catch(function(errArray){
                // 将输出遇到的第一个reject消息
                console.log('errArray', errArray);

            })

    }
}

//demo.displayOneByOne();
demo.displayOnTheSameTime();

 

<think>我们有一个需求:使用JavaScript中的Promise实现递归调用。递归调用在异步操作中需要特别处理,因为我们需要确保每一步的异步操作完成后再进行下一步。 根据引用[2]和引用[4]中的例子,我们可以看到在递归函数中使用Promise时,需要注意返回Promise以及正确处理递归边界和链式调用。 关键点: 1. 在递归函数中,每次调用自身时,都需要返回一个新的Promise,或者利用async/await语法来确保异步操作的顺序执行。 2. 在递归的边界条件中,我们需要解析(resolve)Promise并返回最终结果。 3. 在递归步骤中,我们需要等待异步操作完成,然后继续递归,并将递归返回的Promise继续返回,以保证整个调用链的连续性。 引用[2]中的问题在于,在递归调用aa(++num)时,没有返回该调用返回的Promise,导致递归调用的Promise链断裂。修正方法是在递归调用前加上return。 引用[4]展示了一个正确的递归Promise实现:在递归调用时,返回了递归函数的Promise,这样整个调用链就连接起来了。 下面我们用一个例子来说明如何用Promise实现递归调用。 假设我们有一个异步任务,每次执行需要一定时间,然后我们希望通过递归的方式重复执行这个任务n次,并在所有任务完成后得到最终结果。 我们将创建一个函数`asyncRecursion(n)`,它将递归地执行n次异步任务(每次任务用`asyncTask()`表示,返回一个Promise),并在所有任务完成后解析(resolve)一个包含所有结果的数组。 步骤: 1. 定义异步任务函数`asyncTask()`,它返回一个Promise,模拟异步操作(例如setTimeout)。 2. 定义递归函数`recurse(count, results)`,其中count表示剩余递归次数,results用于累积结果。 3. 在递归函数中,首先检查是否达到递归终止条件(count为0),如果是,则解析(resolve)累积的结果数组。 4. 如果没有终止,则执行异步任务,然后等待其完成,将结果添加到results数组中,然后递归调用`recurse(count-1, results)`,并返回该递归调用的Promise(这样保证链式调用不断)。 5. 注意:每次递归调用都需要返回一个新的Promise,或者使用async函数来自动返回Promise。 这里我们采用async/await语法来简化代码,因为async函数会自动返回Promise。 代码实现: ```javascript // 模拟一个异步任务 function asyncTask() { return new Promise(resolve => { setTimeout(() => { resolve('task done'); }, 100); }); } // 递归函数,使用async/await async function asyncRecursion(n, results = []) { if (n <= 0) { return results; // 终止条件,返回结果数组 } // 执行异步任务并等待完成 const result = await asyncTask(); results.push(result); // 递归调用,并返回递归调用的结果(一个Promise) return asyncRecursion(n - 1, results); } // 调用递归函数 asyncRecursion(5) .then(results => console.log('All done:', results)) .catch(error => console.error(error)); ``` 但是,上面的递归调用会连续执行5次异步任务,每次等待100ms,总时间大约500ms。 另一种不使用async/await,而是直接使用Promise链式调用的方式: ```javascript function asyncRecursion(n, results = []) { return new Promise((resolve, reject) => { if (n <= 0) { resolve(results); return; } asyncTask().then(result => { results.push(result); // 递归调用,返回递归调用的Promise,并继续链式调用 return asyncRecursion(n - 1, results); }).then(resolve).catch(reject); }); } ``` 但是,上面的写法可能会造成多层嵌套的Promise,并且每次递归都新建一个Promise,这在递归深度大时可能会造成性能问题。实际上,我们可以避免在递归函数内部创建新的Promise,而是利用异步任务返回的Promise进行链式调用: ```javascript function asyncRecursion(n, results = []) { if (n <= 0) { return Promise.resolve(results); } return asyncTask().then(result => { results.push(result); return asyncRecursion(n - 1, results); }); } ``` 这种写法更简洁,而且不会创建多余的Promise。它利用了每次调用`asyncTask`返回的Promise,然后在它的then方法中递归调用`asyncRecursion`,并返回递归调用返回的Promise,这样整个调用链就连接起来了。 因此,推荐使用这种方式。 总结一下,使用Promise实现递归调用的要点: 1. 递归函数返回一个Promise(在非终止条件时,返回的是异步任务处理后的Promise链)。 2. 在终止条件时,返回一个已解析(resolve)的Promise,携带最终结果。 3. 在递归步骤中,执行异步任务,然后在它的then方法中递归调用自身,并返回该递归调用返回的Promise(这样链式调用就不会断)。 注意:递归深度过大可能导致栈溢出,因此在实际应用中,如果递归次数非常多,可能需要考虑使用循环(迭代)的方式,或者使用尾递归优化(但JavaScript引擎对尾递归的支持有限)。 现在,我们来回答用户的问题:如何使用Promise实现递归调用? 答案: 使用Promise实现递归调用的关键在于确保每次递归调用都返回一个Promise,并且在异步操作完成后继续递归,同时将递归返回的Promise继续返回,以保持链式调用。下面是一个简单的示例,展示了如何使用Promise进行递归调用: ```javascript // 模拟异步任务 function asyncTask(value) { return new Promise(resolve => { setTimeout(() => resolve(value * 2), 100); }); } // 递归函数,对初始值进行n次异步操作 function recursiveAsync(n, value) { if (n === 0) { // 终止条件,返回一个已解决的Promise,携带最终值 return Promise.resolve(value); } // 执行异步任务,然后递归 return asyncTask(value).then(newValue => { // 递归调用,次数减1,传入新的值 return recursiveAsync(n - 1, newValue); }); } // 调用递归函数,5次递归 recursiveAsync(5, 1).then(result => { console.log('最终结果:', result); // 最终结果: 32(因为1*2^5=32) }); ``` 在这个例子中,我们递归调用了5次,每次对值进行乘以2的异步操作。最终,我们得到1*2^5=32。 另外,我们也可以使用async/await语法来使代码更清晰: ```javascript async function recursiveAsync(n, value) { if (n === 0) { return value; } const newValue = await asyncTask(value); return recursiveAsync(n - 1, newValue); } ``` 使用async/await版本更简洁,但要注意,它仍然是返回一个Promise,并且递归调用时使用await来等待每一步的异步操作。 无论使用哪种方式,都要注意递归深度,避免栈溢出。 如果递归次数非常多,建议使用迭代方式(循环)配合Promise链,或者使用尾递归优化(如果环境支持),但更通用的方法是使用循环和异步迭代控制。 相关问题: 1. 在Promise递归调用中如何处理错误? 2. 如何避免Promise递归调用中的栈溢出问题? 3. 如何使用循环代替递归实现类似的异步操作序列? 4. 在递归Promise调用中如何实现并行执行?</think>### JavaScript Promise 递归实现详解 在 JavaScript 中,使用 Promise 实现递归调用的关键在于**确保每次递归都返回新的 Promise 对象**,并通过 `return` 语句保持 Promise 链的连续性。以下是实现方法和注意事项: #### 核心实现方法 ```javascript // 递归函数必须返回 Promise function recursivePromise(n) { // 终止条件 if (n <= 0) { return Promise.resolve("递归结束"); } // 执行异步操作 return new Promise((resolve) => { setTimeout(() => { console.log(`执行第 ${n} 次操作`); // 关键:返回递归调用的Promise resolve(recursivePromise(n - 1)); }, 1000); }).then(result => result); // 传递结果 } // 调用示例 recursivePromise(3) .then(result => console.log(result)) .catch(err => console.error(err)); ``` #### 使用 async/await 简化版 ```javascript async function asyncRecursive(n) { if (n <= 0) return "递归结束"; await new Promise(resolve => setTimeout(resolve, 1000)); console.log(`执行第 ${n} 次操作`); // 必须返回递归调用结果 return asyncRecursive(n - 1); } // 调用 asyncRecursive(3).then(console.log); ``` #### 关键注意事项 1. **必须返回 Promise** 每次递归调用都需要返回新的 Promise 对象,否则链式调用会中断[^2]。 2. **正确处理终止条件** 终止条件应返回 `Promise.resolve()` 或具体值,避免无限递归。 3. **保持链式连接** 在 Promise 的 `resolve` 中返回递归函数,或在 `then()` 中继续传递结果[^4]。 4. **错误处理** 使用 `.catch()` 统一捕获递归过程中的错误: ```javascript function safeRecursive(n) { if (n <= 0) return Promise.resolve(); return someAsyncTask() .then(() => safeRecursive(n - 1)) .catch(handleError); // 统一错误处理 } ``` #### 实际应用场景 1. **分页数据加载** ```javascript async function fetchAllPages(page = 1, results = []) { const data = await fetch(`/api/data?page=${page}`); results.push(...data.items); if (data.hasMore) { return fetchAllPages(page + 1, results); // 递归下一页 } return results; // 返回所有数据 } ``` 2. **层级数据处理** ```javascript function processTree(node) { return fetchNodeDetails(node.id) .then(details => { return Promise.all( node.children.map(child => processTree(child) // 递归处理子节点 ) ).then(() => details); }); } ``` ### 常见问题解决 **Q: 为什么递归调用后后面的代码不执行?** A: 因为没有返回递归调用的 Promise,导致链式调用断裂[^2]。确保每次递归都 `return` 新的 Promise。 **Q: 如何控制递归深度?** A: 添加终止条件参数: ```javascript function deepRecursion(data, depth = 0, maxDepth = 10) { if (depth > maxDepth) return Promise.reject("超过最大深度"); // ...递归逻辑 } ``` **Q: 如何优化性能?** A: 对于独立操作可使用 `Promise.all()` 并行处理: ```javascript async function parallelRecursion(items) { const promises = items.map(item => item.children ? parallelRecursion(item.children) : processItem(item) ); return Promise.all(promises); } ``` ### 总结 Promise 递归的核心是: 1. 每次递归返回新 Promise 2. 通过 `return` 保持调用链连续 3. 使用 `async/await` 简化代码结构 4. 统一错误处理机制 > 示例代码执行流程: > 当调用 `recursivePromise(3)` 时: > 1. 第一次执行:等待1秒 → 输出"执行第3次操作" > 2. 调用 `recursivePromise(2)` > 3. 第二次执行:等待1秒 → 输出"执行第2次操作" > 4. 调用 `recursivePromise(1)` > 5. 第三次执行:等待1秒 → 输出"执行第1次操作" > 6. 满足终止条件 → 返回"递归结束" > 7. 结果通过 Promise 链传递回初始调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值