Promise相关 及 一些程序输出题

67 阅读7分钟

promise是一个构造函数,promise对象用来封装一个异步操作并可以获取其成功或失败的结果值

为什么要用promise?

  1. promise指定回调函数的方式更加灵活 旧的:必须在异步任务前指定 promise:启动异步任务→返回promise对象→给promise对象绑定回调函数
  2. 支持链式调用,可以解决回调地域的问题 回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件

then catch

在then和catch中,如果正常,返回resolve,报错返回reject

例子

const myPromise = new Promise((resolve, reject) => {
    const success = true;
    if (success) {
        resolve("Operation was successful!");
    } else {
        reject("Operation failed.");
    }
});

**myPromise
    .then((result) => {
        console.log(result); // 如果操作成功,这里会输出 "Operation was successful!"
    })
    .catch((error) => {
        console.error(error); // 如果操作失败,这里会输出 "Operation failed."
    });**

Promise的状态

  1. Pending:待定,初始状态,既没有被解决也没有被拒绝
  2. Fulfilled:已完成,操作成功完成,并返回了结果
  3. Rejected:已拒绝,操作失,并返回了一个原因

Promise的常用方法

Promise.resolve(value)

  1. 如果传入的参数为非promise类型的对象,则返回结果为成功的promise对象。
  2. 如果传入的参数为promise对象,则参数是成功的就返回成功的,失败的就返回失败的
let p1 = Promise.resolve(666) //这个就是 非promise对象

let p2 = Promise.resolve(new Promise((resolve) =>{
	resolve('ok')
}

Promise.reject()

返回一个失败的promise对象

Promise.all()

参数为n个promise对象,当n个promise对象都成功的时候返回一个成功的promise对象,只要有一个失败了就返回失败的promise对象

promise.race()

参数为n个promise对象,第一个完成的promise对象是成功的就返回成功,失败则失败

Promise的习题(事件循环)

1

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve('success')
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

过程:

  1. 先遇到new Promise 执行其中的打印1
  2. 遇到resolve,将promise的状态改为resolve
  3. 打印2
  4. 遇到promise.then这个微任务,将其加入微任务队列,继续向下执行宏任务
  5. 打印4
  6. 宏任务完毕,执行微任务队列,promise此时状态为resolve所以执行then,打印3

结果:1243

2

const fn = () =>
  new Promise((resolve, reject) => {
    console.log(1);
    resolve("success");
  });
console.log("start");

fn().then(res => {
  console.log(res);
});

注意,fn是一个函数的形式,上面只是定义了fn为一个promise,但他没有执行fn().then 的含义为先执行fn这个函数 再定一的then方法,因此执行过程为

'start'
1
'success'

3

const promise = new Promise((resolve, reject) => {
  console.log(1);
  setTimeout(() => {
    console.log("timerStart");
    resolve("success");
    console.log("timerEnd");
  }, 0);
  console.log(2);
});
promise.then((res) => {
  console.log(res);
});
console.log(4);

过程:

  1. 打印1
  2. 遇到了stetimeout 加入宏任务队列
  3. 打印2
  4. 遇到promise.then,但是现在不知道promise的状态,因此先跳过
  5. 打印4
  6. 执行第二个宏任务settimeout,打印timerstart
  7. 将promise的状态设定为成功
  8. 打印timerend
  9. 执行then方法,打印success

答案:

1
2
4
"timerStart"
"timerEnd"
"success"

4

Promise.resolve(1)
  .then((res) => {
    console.log(res)
    return 2
  })
  .catch((err) => {
    return 3
  })
  .then((res) => {
    console.log(res)
  })
  1. 调用promise.resolve传参为1
  2. 执行then 打印1
  3. return 2 的意思是链式调用,继续传递一个Promise.resolve(2)
  4. 执行 打印2
1
2

5

Promise.resolve()
  .then(() => {
    console.log('then')
  })
process.nextTick(() => {
  console.log('nextTick')
})
setImmediate(() => {
  console.log('setImmediate')
})
console.log('end')
  1. 执行第一个宏任务,打印end
  2. 执行微任务队列 nextTick优先级高于promise.then(规定) 打印nextTick
  3. 打印then
  4. 执行下一个宏任务setImmediate
end
nextTick
then
setImmediate

手撕promise.all

Promise.all是返回一个新的Promise对象,如果传入参数里的所有Promise对象都是成功则返回成功,失败则返回失败。

function myPromiseAll(promises) {
    return new Promise((resolve, reject) => {
        if(!Array.isArray(promises)){
            return reject(new TypeError('输入参数需要为数组'))
        }
        //储存Promise结果的数组 和 计数
        const ans = []
        let count = 0

        //如果传入参数是一个空数组,返回一个空的promise正确对象
        if(promises.length === 0) return resolve(ans)

        //遍历每一个promises对象
        promises.forEach((item, index)=> {
            Promise.resolve(item)
	            .then(result => {
	                ans[index] = result
	                count++
	                if(count === promises.length)   resolve(ans)
	            })
	
	            .catch(error=>{
	                 reject(error)
	            })
        })
    })
}

手撕Promise.race

接收一堆Promise 最先返回的Promise对象是成功就返回成功的Promise对象,失败则返回失败的Promise对象.如果传入的参数不是数组,则报错。传入的数组长度为0 直接return

        function myPromiseRace(promises) {
           return new Promise((resolve, reject) => {
                if (!Array.isArray(promises)) {
                    return reject(new TypeError('输入参数需要为数组'))
                }
                
                if (promises.length === 0) return

                promises.forEach(item => {
                    Promise.resolve(item)
                        .then(resolve)
                        .catch(reject)
                })
            })
        }

如何用Promise实现按顺序调用三个接口,以及后续可能的问题

可以利用promise的链式调用,用then,每个返回后都return下一个对应的promise

function api1() {
  return new Promise((resolve) => setTimeout(() => resolve("API1 resolved"), 1000));
}

function api2() {
  return new Promise((resolve) => setTimeout(() => resolve("API2 resolved"), 1000));
}

function api3() {
  return new Promise((resolve) => setTimeout(() => resolve("API3 resolved"), 1000));
}

// 按顺序调用
api1()
  .then((res1) => {
    console.log(res1); // API1 resolved
    return api2();
  })
  .then((res2) => {
    console.log(res2); // API2 resolved
    return api3();
  })
  .then((res3) => {
    console.log(res3); // API3 resolved
  })
  .catch((err) => {
    console.error("Error occurred:", err);
  });

多个接口同时调用怎么办?

在封装完3个api函数后,使用Promise.all进行

Promise.all([api1(), api2(), api3()])
  .then((results) => {
    console.log("All results:", results);
  })
  .catch((err) => {
    console.error("Error in one of the promises:", err);
  });

如何处理某个逻辑的超时逻辑?

封装一个超时函数

function withTimeout(promise, ms) {
  return Promise.race([
    promise,
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error("Timeout")), ms)
    ),
  ]);
}

withTimeout(api1(), 2000)
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

题目

Promise.resolve('A')
  .then('B')
  .then(Promise.resolve('C'))
  .then(console.log);

输出 A

then catch的参数都必须为函数 否则会被忽略

题目

Promise.resolve().then(() => {
  return new Error('error')
}).then(res => {
  console.log("then: ", res)
}).catch(err => {
  console.log("catch: ", err)
})

输出:then: Error: error

new Error不会改变Promise的状态

题目

const newPromise1 = new Promise((resolve, reject) => {
  console.log('A');
  resolve('B');
});
const newPromise2 = newPromise1.then(res => {
  console.log(res);
});
console.log('C', newPromise1);
console.log('D', newPromise2);

先打印A,然后执行resolve,接着遇到newpromise2, 由于里面的.then属于微任务,因此稍后执行,打印C,并且为fufilled状态,打印D,由于未执行then,因此未pending状态

A 
C  fulfilled
D  Pending
B

程序输出题目

Promise.resolve().then(() => {
  console.log('outerPromise');
  const innerTimer = setTimeout(() => {
    console.log('innerTimer')
  }, 0)
});

const timer1 = setTimeout(() => {
  console.log('outerTimer')
  Promise.resolve().then(() => {
    console.log('innerPromise')
  })
}, 0)
console.log('run');

要记住settimeout属于宏任务

run
outerPromise
outerTimer
innerPromise
innerTimer

程序输出题

Promise.resolve('A')
  .then('B')
  .then(Promise.resolve('C'))
  .then(console.log)

如果then接受到的是一个非函数的值,比如字符串或者一个被解析了的Promise对象,那么会忽略他

A

promise1 pending promise2 pending

innerpromise1 fulfilled

innerPromise2 reject

程序输出题

Promise.resolve('A')
  .then(res => {
    console.log(res);
    return 'B';
  })
  .catch(err => {
    return 'C';
  })
  .then(res => {
    console.log(res);
  });

链式调用中,return的话会把值进行传递

A
B

程序输出题

Promise.reject('error')
  .then((res) => {
    console.log('succeed', res)
  }, (err) => {
    console.log('innerError', err)
  }).catch(err => {
    console.log('catch', err)
  })

在then中,接受了两个参数,一个是成功,一个是失败。当接受失败的实行了,then往下传递的是一个成功的Promise,因此catch不会被执行

innerError error

程序输出题

Promise.resolve('A')
  .then(res => {
    console.log('promise1', res)
  })
  .finally(() => {
    console.log('finally1')
  })
Promise.resolve('B')
  .finally(() => {
    console.log('finally2')
    return 'result'
  })
  .then(res => {
    console.log('promise2', res)
  })

先执行同步代码,

创建Promise,并resolve(A),遇到then,将其加入微任务队列,

继续执行同步代码,Promise.resolve(B)

同步代码执行完,执行微任务,输出 promise1 A

遇到finally1,加入微任务队列

按次序执行微任务,输出finally2 ,遇到.then 继续加入微任务队列

按次序,输出finally1 再输出promise2

另外,finally,不会改变链式调用中后续的promise的值

promise1 A
finally2
finally1
promise2 B

promiseAll的程序输出

function runAsync(num) {
  return new Promise(
    r => setTimeout(
      () => r(num, console.log(num)
      ), 1000)
  )
}

Promise.all([runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log(res));

promiseAl的程序输出

function runAsync(num) {
  return new Promise((resolve) => setTimeout(
    () => resolve(num, console.log(num)), 1000)
  );
}

function runReject(num) {
  return new Promise((resolve, reject) => setTimeout(
    () => reject(`Error: ${num}`, console.log(num)), 1000 * num)
  );
}

Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
  .then((res) => console.log(res))
  .catch((err) => console.log(err));

console.log 由于是一个表达式,因此会直接输出出来,但也不影响它作为一个类似变量的东西进行传递。

promiseAll,如果遇到reject的会返回出第一个遇到的错误的promise。成功的话会返回所有成功的

1
3
2
Error: 2
4