JavaScript中的Promise详解
1. 手写Promise实现
1.1 Promise基本声明
Promise是一个类,它的构造函数接受一个函数作为参数,该函数的两个参数也都是函数
class MyPromise {
// 构造器
constructor(executor) {
const resolve = () => {};
const reject = () => {};
// 立即执行
executor(resolve, reject);
}
}
1.2 Promise的三种状态
Promise存在三个状态(state):
- pending(待定):Promise初始状态,在resolve或reject前处于此状态
- fulfilled(已成功):Promise被resolve后处于此状态,状态不能再改变,且必须拥有一个不可变的值(value)
- rejected(已失败):Promise被reject后处于此状态,状态也不能再改变,且必须拥有一个不可变的拒绝原因(reason)
class MyPromise {
constructor(executor) {
// 初始化state为等待态
this.state = 'pending';
// 成功的值
this.value = undefined;
// 失败的原因
this.reason = undefined;
const resolve = (value) => {
// state改变,resolve调用就会失败
if (this.state === 'pending') {
// resolve调用后,state转化为成功态
this.state = 'fulfilled';
// 储存成功的值
this.value = value;
}
};
const reject = (reason) => {
// state改变,reject调用就会失败
if (this.state === 'pending') {
// reject调用后,state转化为失败态
this.state = 'rejected';
// 储存失败的原因
this.reason = reason;
}
};
// 如果executor执行报错,直接执行reject
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
}
1.3 then方法
Promise有一个叫做then的方法,then方法是定义在原型对象Promise.prototype上的方法,它的作用是为Promise实例添加状态改变时的回调函数。
then方法接受两个参数:
- onFulfilled:当Promise对象状态变为fulfilled时调用
- onRejected:当Promise对象状态变为rejected时调用
class MyPromise {
constructor(executor) {
// ...
}
// then 方法 有两个参数onFulfilled onRejected
then(onFulfilled, onRejected) {
// 状态为fulfilled,执行onFulfilled,传入成功的值
if (this.state === 'fulfilled') {
onFulfilled(this.value);
}
// 状态为rejected,执行onRejected,传入失败的原因
if (this.state === 'rejected') {
onRejected(this.reason);
}
}
}
const test = new MyPromise((resolve, reject) => {
resolve('成功');
}).then((res) => console.log(res), (err) => console.log(err));
// 成功
1.4 静态方法
是当resolve在setTimeout内执行,then时state还是pending等待状态 我们就需要在then调用的时候,将成功和失败存到各自的数组,一旦reject或者resolve,就调用它们
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功存放的数组
this.onResolvedCallbacks = [];
// 失败存放法数组
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 一旦resolve执行,调用成功数组的函数
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 一旦reject执行,调用失败数组的函数
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
}
if (this.state === 'rejected') {
onRejected(this.reason);
}
// 当状态state为pending时
if (this.state === 'pending') {
// onFulfilled传入到成功数组
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value);
});
// onRejected传入到失败数组
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}
const test = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
});
}).then((res) => console.log(res), (err) => console.log(err));
console.log('失败');
// 如果是1.4 由于立即执行函数,没有办法解决异步执行resolve代码,返回 失败
// 失败 -> 成功
1.5 链式调用实现
then方法可以被同一个Promise调用多次:
- 当Promise成功执行时,所有onFulfilled回调需按照其注册顺序依次执行
- 当Promise被拒绝执行时,所有onRejected回调需按照其注册顺序依次执行
then方法必须返回一个新的Promise对象,以实现链式调用:
- 如果onFulfilled或onRejected返回一个值x,则运行Promise解决过程:[Resolve](promise2, x) resolvePromise
- 如果onFulfilled或onRejected抛出一个异常e,则promise2必须拒绝执行,并返回拒因e
- 如果onFulfilled不是函数且promise1成功执行,promise2必须成功执行并返回相同的值
- 如果onRejected不是函数且promise1拒绝执行,promise2必须拒绝执行并返回相同的据因
class MyPromise {
constructor(executor) {
// ...
}
then(onFulfilled, onRejected) {
// 声明返回的promise2
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
const x = onFulfilled(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
}
if (this.state === 'rejected') {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
});
this.onRejectedCallbacks.push(() => {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
});
}
});
// 返回promise,完成链式
return promise2;
}
}
1.6 resolvePromise函数详解
resolvePromise函数用于处理then方法返回值x的不同情况:
-
基础类型值处理:
- 如果x是普通值(非对象或函数),直接调用resolve(x)
- x不能是null
-
对象或函数处理(包括Promise):
- 如果x是对象或函数,声明
let then = x.then - 如果取then属性时报错,则调用reject()
- 如果then是函数,则使用call执行then,第一个参数是this,后面是成功回调和失败回调
- 如果成功回调返回的还是Promise,就递归继续解析
- 如果x是对象或函数,声明
-
防止多次调用:
- 成功和失败回调只能调用一个,所以设定一个called变量来防止多次调用
/**
* Promise解决过程 [[Resolve]](promise, x)
* @param {Promise} promise2 - then方法返回的新Promise对象
* @param {*} x - then方法回调函数的返回值
* @param {Function} resolve - promise2的resolve函数
* @param {Function} reject - promise2的reject函数
*/
function resolvePromise(promise2, x, resolve, reject) {
// 规范 2.3.1,x 不能和 promise2 相同,避免循环引用
if (x === promise2) {
// reject报错
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 规范 2.3.2
// 如果 x 为 Promise,状态为 pending 需要继续等待否则执行
if (x instanceof MyPromise) {
// 2.3.2.1 如果x为pending状态,promise必须保持pending状态,直到x为fulfilled/rejected
if (x.currentState === 'pending') {
x.then((value) => {
// 再次调用该函数是为了确认 x resolve 的
// 参数是什么类型,如果是基本类型就再次 resolve
// 把值传给下个 then
resolutionProcedure(promise2, value, resolve, reject);
}, reject);
} else { // 但如果这个promise的状态已经确定了,那么它肯定有一个正常的值,而不是一个thenable,所以这里可以取它的状态
x.then(resolve, reject);
}
return;
}
// 防止多次调用
let called;
// 规范 2.3.3,判断 x 是否为对象或函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 规范 2.3.3.2,如果不能取出 then,就 reject
try {
// 规范2.3.3.1 因为x.then可能是一个getter,这种情况下多次读取就有可能产生副作用
// 既要判断它的类型,又要调用它,这就是两次读取
const { then } = x;
// 规范2.3.3.3,如果 then 是函数,调用 x.then
if (typeof then === 'function') {
// 规范 2.3.3.3
// reject 或 reject 其中一个执行过的话,忽略其他的
then.call(x, (y) => {
// 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
}, (err) => {
if (called) return;
called = true;
reject(err); // 失败了就失败了
});
} else {
resolve(x); // 直接成功即可
}
} catch (e) {
// 也属于失败
if (called) return;
called = true;
// 取then出错了那就不要在继续执行了
reject(e);
}
} else {
resolve(x);
}
}
1.7 其他问题
2.1 onFulfilled和onRejected参数可选性
- onFulfilled和onRejected都是可选参数
- 如果类型不是函数需要忽略,后续如果还有then的回调会继续执行
- 都有默认值,具体如下:
- onFulfilled的默认值是
value => value - onRejected的默认值是
err => { throw err; }
- onFulfilled的默认值是
- 异步调用要求:
- 规定onFulfilled或onRejected不能同步被调用,必须异步调用
- 我们使用setTimeout解决异步问题,确保回调在下一个事件循环中执行
- 如果onFulfilled或onRejected报错,则直接返回reject()
class MyPromise {
constructor(executor) {
// ...
}
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
// onRejected如果不是函数,就忽略onRejected,直接扔出错误
onRejected = typeof onRejected === 'function' ? onRejected : (err) => {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 异步
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === 'rejected') {
// 异步
setTimeout(() => {
// 如果报错
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
// 至于为什么用 setTimeout?因为我们模拟不了微任务,那就用宏任务去解决吧
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
// 异步
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
// 返回promise,完成链式
return promise2;
}
}
1.8 测试Promise
为了确保我们实现的MyPromise符合Promise/A+规范,我们需要使用官方测试套件进行验证。
1.8.1 实现deferred方法
测试套件要求实现一个静态方法deferred,用于创建一个包含resolve、reject方法和promise对象的结构。该方法的实现如下:
/**
* 创建一个deferred对象,包含promise、resolve和reject属性
* 用于Promise/A+测试套件
* @returns {Object} deferred对象
* @property {MyPromise} promise - Promise实例
* @property {Function} resolve - resolve函数
* @property {Function} reject - reject函数
*/
// eslint-disable-next-line no-multi-assign
MyPromise.defer = MyPromise.deferred = function () {
const dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = MyPromise;
1.8.2 运行Promises/A+测试套件
首先全局安装Promise/A+测试套件:
npm install promises-aplus-tests -g
然后在package.json中添加测试脚本:
{
"devDependencies": {
"promises-aplus-tests": "^2.1.2"
},
"scripts": {
"test": "promises-aplus-tests MyPromise"
}
}
执行测试命令:
npm test
通过测试后,可以确保我们的MyPromise实现完全符合Promise/A+规范。
1.9 Promise.all
MyPromise.all = function(promiseList) {
const resPromise = new MyPromise((resolve, reject) => {
let count = 0;
const result = [];
const { length } = promiseList;
if (length === 0) {
return resolve(result);
}
promiseList.forEach((promise, index) => {
MyPromise.resolve(promise).then((value) => {
count += 1;
result[index] = value;
if (count === length) {
resolve(result);
}
}, (reason) => {
reject(reason);
});
});
});
return resPromise;
};
1.10 Promise.race
MyPromise.race = function(promiseList) {
const resPromise = new MyPromise((resolve, reject) => {
const { length } = promiseList;
if (length === 0) {
return resolve();
}
for (let i = 0; i < length; i++) {
MyPromise.resolve(promiseList[i]).then((value) => {
return resolve(value);
}, (reason) => {
return reject(reason);
});
}
});
return resPromise;
};
1.11 完整代码
function resolvePromise(promise2, x, resolve, reject) {
// 规范 2.3.1,x 不能和 promise2 相同,避免循环引用
if (x === promise2) {
// reject报错
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 规范 2.3.2
// 如果 x 为 Promise,状态为 pending 需要继续等待否则执行
if (x instanceof MyPromise) {
// 2.3.2.1 如果x为pending状态,promise必须保持pending状态,直到x为fulfilled/rejected
if (x.currentState === 'pending') {
x.then((value) => {
// 再次调用该函数是为了确认 x resolve 的
// 参数是什么类型,如果是基本类型就再次 resolve
// 把值传给下个 then
resolutionProcedure(promise2, value, resolve, reject);
}, reject);
} else { // 但如果这个promise的状态已经确定了,那么它肯定有一个正常的值,而不是一个thenable,所以这里可以取它的状态
x.then(resolve, reject);
}
return;
}
// 防止多次调用
let called;
// 规范 2.3.3,判断 x 是否为对象或函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 规范 2.3.3.2,如果不能取出 then,就 reject
try {
// 规范2.3.3.1 因为x.then可能是一个getter,这种情况下多次读取就有可能产生副作用
// 既要判断它的类型,又要调用它,这就是两次读取
const { then } = x;
// 规范2.3.3.3,如果 then 是函数,调用 x.then
if (typeof then === 'function') {
// 规范 2.3.3.3
// reject 或 reject 其中一个执行过的话,忽略其他的
then.call(x, (y) => {
// 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
}, (err) => {
if (called) return;
called = true;
reject(err);// 失败了就失败了
});
} else {
resolve(x); // 直接成功即可
}
} catch (e) {
// 也属于失败
if (called) return;
called = true;
// 取then出错了那就不要在继续执行了
reject(e);
}
} else {
resolve(x);
}
}
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功存放的数组
this.onResolvedCallbacks = [];
// 失败存放法数组
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 一旦resolve执行,调用成功数组的函数
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 一旦reject执行,调用失败数组的函数
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
// onRejected如果不是函数,就忽略onRejected,直接扔出错误
onRejected = typeof onRejected === 'function' ? onRejected : (err) => {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 异步
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === 'rejected') {
// 异步
setTimeout(() => {
// 如果报错
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
// 至于为什么用 setTimeout?因为我们模拟不了微任务,那就用宏任务去解决吧
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
// 异步
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
// 返回promise,完成链式
return promise2;
}
}
MyPromise.resolve = function(val) {
return new MyPromise((resolve, reject) => {
resolve(val);
});
};
MyPromise.reject = function(val) {
return new MyPromise((resolve, reject) => {
reject(val);
});
};
MyPromise.race = function(promiseList) {
const resPromise = new MyPromise((resolve, reject) => {
const { length } = promiseList;
if (length === 0) {
return resolve();
}
for (let i = 0; i < length; i++) {
MyPromise.resolve(promiseList[i]).then((value) => {
return resolve(value);
}, (reason) => {
return reject(reason);
});
}
});
return resPromise;
};
MyPromise.all = function(promiseList) {
const resPromise = new MyPromise((resolve, reject) => {
let count = 0;
const result = [];
const { length } = promiseList;
if (length === 0) {
return resolve(result);
}
promiseList.forEach((promise, index) => {
MyPromise.resolve(promise).then((value) => {
count += 1;
result[index] = value;
if (count === length) {
resolve(result);
}
}, (reason) => {
reject(reason);
});
});
});
return resPromise;
};
2. Promise面试题
2.1 promise基础
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
reject('fail');
}, 0);
resolve();
console.log(2);
});
promise.then(() => {
console.log(3);
}).catch(() => {
console.log(4);
});
console.log(5);
// 输出结果 1 2 5 3
// Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的
// 构造函数中的 resolve 或 reject 只有第一次执行有效,状态一旦改变则不能再变
2.2 promise 链式调用
Promise.resolve(1)
.then((res) => {
console.log(res);
throw Error('Oh no!');
// eslint-disable-next-line no-unreachable
return 2;
})
.then((res) => {
// 由于报错跳过
console.log(res);
return 3;
})
.catch((err) => {
console.log('fail', err);
return 4;
})
.catch((err) => {
// 由于返回 resolve 跳过
console.log(err);
return 5;
})
.then((res) => {
console.log(res);
})
.then((res) => {
// 上一个then返回的值为undefined
console.log(res);
});
// 输出结果 1 fail Error: Oh no! 4 undefined
// promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用
// 如果当前步骤遇到错误,则任何后续的 .then 都将被跳过,直到遇到 .catch
Promise.resolve()
.then(() => {
return new Error('Oh no!');
})
.then((res) => {
console.log('then: ', res);
})
.catch((err) => {
console.log('catch: ', err);
});
// 输出结果 then: Oh no!
// .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获,需要改成其中一种
// return Promise.reject(new Error('Oh no!'))
// throw Error('Oh no!')
// 因为返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return new Error('Oh no!') 等价于 return Promise.resolve(new Error('Oh no!'))
const promise = Promise.resolve()
.then(() => {
return promise;
});
promise.catch(console.error);
// 输出结果 TypeError: Chaining cycle detected for promise
// .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log);
// 1
// .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透
- Promise的状态一经改变就不能再改变
- .then和.catch都会返回一个新的Promise
- catch不管被连接到哪里,都能捕获上层未捕捉过的错误
- 在Promise中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如return 2会被包装为return Promise.resolve(2)。
- Promise 的 .then 或者 .catch 可以被调用多次, 但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch的时候都会直接拿到该值。
- .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获
- .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
- .then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传
- .then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch是.then第二个参数的简便写法
- .finally方法也是返回一个Promise,他在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数
Promise.resolve('1')
.then((res) => {
console.log(res);
return 2;
})
.then((res) => {
console.log(res);
return 3;
})
.finally(() => {
console.log('finally');
});
Promise.resolve('4')
.then((res) => {
console.log('finally2', res);
return 5;
})
.finally(() => {
console.log('finally2');
return 6;
})
.then((res) => {
console.log('finally2', res);
});
// 1 finally2 4 2 finally2 finally finally2 5
// finally与then和catch相同,都是返回promise,都是微任务
2.3 setTimeout相关
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('once');
resolve('success');
}, 1000);
});
const start = Date.now();
promise.then((res) => {
console.log(res, Date.now() - start);
});
promise.then((res) => {
console.log(res, Date.now() - start);
});
// once success 1005 success 1007
// promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,
// 那么后续每次调用 .then 或者 .catch 都会直接拿到该值。
2.4 race all等
function runAsync (x) {
const p = new Promise((r) => setTimeout(() => r(x, console.log(x)), 1000));
return p;
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
.then((res) => console.log('result: ', res))
.catch((err) => console.log(err));
// 1 'result: ' 1 2 3
// 使用.race()方法,它只会获取最先执行完成的那个结果,其它的异步任务虽然也会继续进行下去,不过race已经不管那些任务的结果了
2.5 async/await
async function async1 () {
console.log('async1 start');
await async2();
console.log('async1 end');
setTimeout(() => {
console.log('timer1');
}, 0);
}
async function async2 () {
setTimeout(() => {
console.log('timer2');
}, 0);
console.log('async2');
}
async1();
setTimeout(() => {
console.log('timer3');
}, 0);
console.log('start');
// async1 start => async2 => start => async1 end => timer2 => timer3 => timer1
async function async1 () {
await async2();
console.log('async1');
return 'async1 success';
}
async function async2 () {
return new Promise((resolve, reject) => {
console.log('async2');
reject('error');
});
}
async1().then((res) => console.log(res));
// 'async2' => Uncaught (in promise) error
// 如果在async函数中抛出了错误,则终止错误结果,不会继续向下执行
2.6 综合
const async1 = async () => {
console.log('async1');
setTimeout(() => {
console.log('timer1');
}, 2000);
await new Promise((resolve) => {
console.log('promise1');
});
console.log('async1 end');
return 'async1 success';
};
console.log('script start');
async1().then((res) => console.log(res));
console.log('script end');
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.catch(4)
.then((res) => console.log(res));
setTimeout(() => {
console.log('timer2');
}, 1000);
// script start => async1 => promise1 => script end => 1 => timer2 => timer1
// async函数中await的new Promise要是没有返回值的话则不执行后面的内容
// .then函数中的参数期待的是函数,如果不是函数的话会发生透传
// 注意定时器的延迟时间