这套题还不错,感兴趣的猿可以试一试:前端开发工程师

1、如何实现一个 sleep 函数(延迟函数)

通过 promisesetTimeout 来简单实现

/**
 * 延迟函数
 * @param {Number} time 时间
 */
function sleep (time = 1500) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(true)
        }, time)
    })
}
 

2、promise 构造函数、then 方法、catch 方法、finally 方法哪个异步哪个同步?

promise 构造函数是同步执行的,thencatchfinally 方法是异步执行的。

3、如何取消一个 promise

1. 使用 promise.race()

  • Promise.race(iterable)

iterable 参数里的任意一个子 promise 被成功或失败后,父 promise 马上也会用子 promise 的成功返回值或失败详情作为参数调用父 promise 绑定的相应句柄,并返回该 promise 对象。

/**
* @author guoqiankunmiss
*/
//封装一个取消promise的函数,使用promise.race的特性
function stopPromise (stopP) {
	let proObj = {};
	let promise = new Promise((resolve, reject) => {
		proObj.resolve = resolve;
		proObj.reject = reject;
	})
	proObj.promise = Promise.race([stopP, promise])
	return proObj
}
//一个5秒钟之后执行的.then方法的promise
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(123);
    }, 5000);
});
//调用函数
let obj = stopPromise(promise);
//收集返回值
obj.promise.then(res => {
    console.log(res);
});
//两秒钟之后进行取消promise操作
setTimeout(() => {
	obj.resolve("Promise 请求被取消了!");
}, 2000)
 

4、多个 promise 如何获取第一个成功promise

1. Promise.all 改进

利用 promise.all 的特性,遍历 promise 数组,根据返回值进行判断,当成功的时候,转为 reject 返回,当失败的时候转为 resolve 继续执行。

//第一个成功的Promise
function firstProSuccess (allProMise) {
  //遍历promise数组,根据返回值进行判断,当成功的时候,转为reject返回,当失败的时候转为resolve继续执行。
  return Promise.all(allProMise.map(item => {
    return item.then(
      res => Promise.reject(res),
      err => Promise.resolve(err)
    )
  })).then(
    errors => Promise.reject(errors),
    val => Promise.resolve(val)
  )
}
 

2. Promise.any

  • Promise.any(iterable)

接收一个 Promise 对象的集合,当其中的一个 promise 成功,就返回那个成功的 promise 的值。

缺点:有兼容问题

5、多个 promise,所有的 promise 都取得返回结果(不管成功/失败都要返回值)

1. Promise.all 改进

和上面原理类似,只不过是当成功的时候不进行操作,当 reject 时进行 resolve 操作

2. Promise.allSettled()

  • Promise.allSettled(iterable)

返回一个在所有给定的 promise 都已经 fulfilledrejected 后的 promise

缺点:有兼容问题

6、说说 promise 的静态方法有哪些?

1. Promise.all(iterable)

接收一个 promise 数组对象(可迭代的 promise 实例对象),全部成功时,返回所有 promise 的数组集合;当其中一个失败时,返回当前失败的 promise 对象。

2. Promise.allSettled(iterable)

接收一个 promise 数组对象,全部完成时(不管成功/失败)返回新的 promise 数组集合

3. Promise.any(iterable)

接收一个 promise 数组对象,当其中任何一个成功时,返回成功的 promise

4. Promise.race(iterable)

接收一个 promise 数组对象,当其中任意一个成功/失败时,返回该 promise

5. Promise.reject(reason)

返回一个状态为失败的 Promise 对象。

6. Promise.resolve(value)

返回一个状态由给定 value 决定的 Promise 对象。

7. Promise.finally(onFinally)

在当前 promise 运行完毕后被调用,无论当前 promise 的状态是完成( fulfilled )还是失败( rejected )

8. Promise.try(f)

接收一个函数,返回一个 promise

为所有操作提供了统一的处理机制,所以如果想用 then 方法管理流程,最好都用 Promise.try 包装一下。

  • 更好的错误处理
  • 更好的互操作性
  • 易于浏览

7、Promise.then 的第二个参数有了解吗?和 .catch 有什么区别?

then() 方法返回一个 Promise

它最多需要有两个参数Promise 的成功和失败情况的回调函数。

p.then(onFulfilled[, onRejected]);
p.then(value => {
  // fulfillment
}, reason => {
  // rejection
});
 

第二个参数也是一个函数,是对失败情况的回调函数。

then 第二个参数 catch
then 方法的参数 Promise 的实例方法
then 的第一个参数抛出异常捕获不到 then 的第一个参数抛出异常可以捕获
是一个函数 本质是 then 方法的语法糖
如果第二个参数和 catch 同时存在,promise 内部报错,第二个参数可以捕获 此时,catch 捕获不到,第二个参数不存在,catch 才会捕获到
不建议使用 建议使用 catch 进行错误捕获

8、Promise.resolve 有几种情况?

1. 参数是一个 Promise 实例

参数是 Promise 实例,那么 Promise.resolve 将不做任何修改、原封不动地返回这个实例。

2. 参数是一个 thenable 对象

Promise.resolve() 方法会将这个对象转为 Promise 对象,然后就立即执行 thenable 对象的 then() 方法。

3. 参数不是具有 then() 方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有 then() 方法的对象,则 Promise.resolve() 方法返回一个新的 Promise 对象,状态为 resolved

4. 不带有任何参数

直接返回一个 resolved 状态的 Promise 对象。

9、如果 .then 中的参数不是函数,那会怎样?

Promise.resolve(1)
    .then(2)
    .then(console.log)
// 1
 

如果 .then 中的参数不是函数,则会在内部被替换为 (x) => x,即原样返回 promise 最终结果的函数。

10、如果 .finally 后面继续跟了个 .then,那么这个 then 里面的值是什么?

Promise.resolve('resolve')
  .finally(() => {
    console.log('this is finally')
    return 'finally value'
  })
  .then(res => {
    console.log('finally后面的then函数, res的值为:', res)
  })
// this is finally
 

finally 后面的 then 函数, res 的值为: resolve

  1. finally 的回调函数中不接收任何参数;
  2. promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行 finally 回调函数;
  3. finally 返回的是一个上一次的 Promise 对象值。

11、.all.race 在传入的数组有第一个抛出异常的时候,其他异步任务还会继续执行吗?

会的,会继续执行,只是不会在 then / catch 中表现出来。

浏览器执行下面代码,可以看出当报错的时候 console 还是会继续执行的,只是在 对应的回调函数里面没有表现出来。

function sleep (n) {
    return new Promise((resolve, reject) => {
        console.log(n)
        Math.random() > 0.5 ? reject(n) : resolve(n)
    }, n % 2 === 0 ? 1000 * n : 1000)
}
Promise.all([sleep(1), sleep(2), sleep(3)])
  .then(res => console.log('all res: ', res))
  .catch(err => console.log('all err:', err))
Promise.race([sleep(1), sleep(2), sleep(3)])
  .then(res => console.log('race res: ', res))
  .catch(err => console.log('race err:', err))
 

12、.all 是并发的还是串行的?

是并发的,但是返回值和 promise.all 中接收到的数组顺序一样。

13、promise 为什么可以进行链式调用

因为 thencatchfinally 方法会返回一个新的 promise,所以允许我们进行链式调用。

14、async/await

1. 实现原理

async 函数是基于 generator 实现,所以涉及到 generator 相关知识。 在没有async 函数之前,通常使用 co 库来执行 generator,所以通过 co 我们也能模拟 async 的实现。

2. 简单实现

1)co
function Asyncfn() {
  return co(function*() {
    //.....
  });
}
function co(gen) {
  return new Promise((resolve, reject) => {
    const fn = gen();
    function next(data) {
      let { value, done } = fn.next(data);
      if (done) return resolve(value);
      Promise.resolve(value).then(res => {
        next(res);
      }, reject);
    }
    next();
  });
}
 
2)Generator 函数和自执行器
function spawn(genF) {
    return new Promise(function(resolve, reject) {
        const gen = genF();
        function step(nextF) {
            let next;
            try {
                next = nextF();
            } catch (e) {
                return reject(e);
            }
            if (next.done) {
                return resolve(next.value);
            }
            Promise.resolve(next.value).then(
                function(v) {
                    step(function() {
                        return gen.next(v);
                    });
                },
                function(e) {
                    step(function() {
                        return gen.throw(e);
                    });
                }
            );
        }
        step(function() {
            return gen.next(undefined);
        });
    });
}