在一定的时间后,我怎样才能使promise 失效?

我是少了一个还是包装不同了?

或者,下面的实现在不占用内存方面是否很好,是否真的能按预期工作?

我还可以将其以某种方式进行全局包装,这样我就可以将其用于我创建的每个promise ,而不必重复setTimeout和clearTimeout代码吗?

function run() {
    logger.info('DoNothingController working on process id {0}...'.format(process.pid));

    myPromise(4000)
        .then(function() {
            logger.info('Successful!');
        })
        .catch(function(error) {
            logger.error('Failed! ' + error);
        });
}

function myPromise(ms) {
    return new Promise(function(resolve, reject) {
        var hasValueReturned;
        var promiseTimeout = setTimeout(function() {
            if (!hasValueReturned) {
                reject('Promise timed out after ' + ms + ' ms');
            }
        }, ms);

        // Do something, for example for testing purposes
        setTimeout(function() {
            resolve();
            clearTimeout(promiseTimeout);
        }, ms - 2000);
    });
}

谢谢

推荐答案

本机JavaScriptpromise 没有任何超时机制.

关于您的实现的问题可能更适合http://codereview.stackexchange.com人,但有几点需要注意:

  1. 你没有提供在promise 中实际做任何事情的方法

  2. setTimeout回调中不需要clearTimeout,因为setTimeout会安排一个一次性计时器.

  3. 既然promise 一旦被解决/拒绝就不能被解决/拒绝,你就不需要那张支票了.

继续你的myPromise函数方法,也许是这样的:

function myPromise(timeout, callback) {
    return new Promise((resolve, reject) => {
        // Set up the timeout
        const timer = setTimeout(() => {
            reject(new Error(`Promise timed out after ${timeout} ms`));
        }, timeout);

        // Set up the real work
        callback(
            (value) => {
                clearTimeout(timer);
                resolve(value);
            },
            (error) => {
                clearTimeout(timer);
                reject(error);
            }
        );
    });
}

用法如下:

myPromise(2000, (resolve, reject) => {
    // Real work is here
});

(或者可能稍微不那么复杂,请参见下面的分隔符.)

我有点担心语义是不同的(不是new,而是Promise构造函数使用new).但更大的问题是,它假设你总是从头开始创建promise ,但你通常希望能够使用你已经拥有的promise .

您可以通过将Promise子类化来处理这两个问题:

class MyPromise extends Promise {
    constructor(timeout, callback) {
        // We need to support being called with no milliseconds
        // value, because the various Promise methods (`then` and
        // such) correctly call the subclass constructor when
        // building the new promises they return.
        const haveTimeout = typeof timeout === "number";
        const init = haveTimeout ? callback : timeout;
        super((resolve, reject) => {
            if (haveTimeout) {
                const timer = setTimeout(() => {
                    reject(new Error(`Promise timed out after ${timeout}ms`));
                }, timeout);
                init(
                    (value) => {
                        clearTimeout(timer);
                        resolve(value);
                    },
                    (error) => {
                        clearTimeout(timer);
                        reject(error);
                    }
                );
            } else {
                init(resolve, reject);
            }
        });
    }
    // Pick your own name of course. (You could even override `resolve` itself
    // if you liked; just be sure to do the same arguments detection we do
    // above in the constructor, since you need to support the standard use of
    // `resolve`.)
    static resolveWithTimeout(timeout, x) {
        if (!x || typeof x.then !== "function") {
            // `x` isn't a thenable, no need for the timeout,
            // fulfill immediately
            return this.resolve(x);
        }
        return new this(timeout, x.then.bind(x));
    }
}

用法(如果构建新promise ):

let p = new MyPromise(300, (resolve, reject) => {
    // ...
});
p.then((value) => {
    // ...
})
.catch((error) => {
    // ...
});

用法(如果使用您已有的promise ):

MyPromise.resolveWithTimeout(100, somePromiseYouAlreadyHave)
.then((value) => {
    // ...
})
.catch((error) => {
    // ...
});

Live Example:

"use strict";
    
class MyPromise extends Promise {
    constructor(timeout, callback) {
        // We need to support being called with no milliseconds
        // value, because the various Promise methods (`then` and
        // such) correctly call the subclass constructor when
        // building the new promises they return.
        const haveTimeout = typeof timeout === "number";
        const init = haveTimeout ? callback : timeout;
        super((resolve, reject) => {
            if (haveTimeout) {
                const timer = setTimeout(() => {
                    reject(new Error(`Promise timed out after ${timeout}ms`));
                }, timeout);
                init(
                    (value) => {
                        clearTimeout(timer);
                        resolve(value);
                    },
                    (error) => {
                        clearTimeout(timer);
                        reject(error);
                    }
                );
            } else {
                init(resolve, reject);
            }
        });
    }
    // Pick your own name of course. (You could even override `resolve` itself
    // if you liked; just be sure to do the same arguments detection we do
    // above in the constructor, since you need to support the standard use of
    // `resolve`.)
    static resolveWithTimeout(timeout, x) {
        if (!x || typeof x.then !== "function") {
            // `x` isn't a thenable, no need for the timeout,
            // fulfill immediately
            return this.resolve(x);
        }
        return new this(timeout, x.then.bind(x));
    }
}

// Some functions for the demonstration
const neverSettle = () => new Promise(() => {});
const fulfillAfterDelay = (delay, value) => new Promise((resolve) => setTimeout(resolve, delay, value));
const rejectAfterDelay = (delay, error) => new Promise((resolve, reject) => setTimeout(reject, delay, error));

const examples = [
    function usageWhenCreatingNewPromise1() {
        console.log("Showing timeout when creating new promise");
        const p = new MyPromise(100, (resolve, reject) => {
            // We never resolve/reject, so we test the timeout
        });
        return p.then((value) => {
            console.log(`Fulfilled: ${value}`);
        })
        .catch((error) => {
            console.log(`Rejected: ${error}`);
        });
    },

    function usageWhenCreatingNewPromise2() {
        console.log("Showing when the promise is fulfilled before the timeout");
        const p = new MyPromise(100, (resolve, reject) => {
            setTimeout(resolve, 50, "worked");
        });
        return p.then((value) => {
            console.log(`Fulfilled: ${value}`);
        })
        .catch((error) => {
            console.log(`Rejected: ${error}`);
        });
    },

    function usageWhenCreatingNewPromise3() {
        console.log("Showing when the promise is rejected before the timeout");
        const p = new MyPromise(100, (resolve, reject) => {
            setTimeout(reject, 50, new Error("failed"));
        });
        return p.then((value) => {
            console.log(`Fulfilled: ${value}`);
        })
        .catch((error) => {
            console.log(`Rejected: ${error}`);
        });
    },

    function usageWhenYouAlreadyHaveAPromise1() {
        console.log("Showing timeout when using a promise we already have");
        return MyPromise.resolveWithTimeout(100, neverSettle())
        .then((value) => {
            console.log(`Fulfilled: ${value}`);
        })
        .catch((error) => {
            console.log(`Rejected: ${error}`);
        });
    },

    function usageWhenYouAlreadyHaveAPromise2() {
        console.log("Showing fulfillment when using a promise we already have");
        return MyPromise.resolveWithTimeout(100, fulfillAfterDelay(50, "worked"))
        .then((value) => {
            console.log(`Fulfilled: ${value}`);
        })
        .catch((error) => {
            console.log(`Rejected: ${error}`);
        });
    },

    function usageWhenYouAlreadyHaveAPromise3() {
        console.log("Showing rejection when using a promise we already have");
        return MyPromise.resolveWithTimeout(100, rejectAfterDelay(50, new Error("failed")))
        .then((value) => {
            console.log(`Fulfilled: ${value}`);
        })
        .catch((error) => {
            console.log(`Rejected: ${error}`);
        });
    },

    async function usageInAnAsyncFunction1() {
        console.log("Showing timeout in async function");
        try {
            const value = await MyPromise.resolveWithTimeout(100, neverSettle());
            console.log(`Fulfilled: ${value}`);
        } catch (error) {
            console.log(`Rejected: ${error}`);
        }
    },

    async function usageInAnAsyncFunction2() {
        console.log("Showing fulfillment in async function");
        try {
            const value = await MyPromise.resolveWithTimeout(100, fulfillAfterDelay(50, "worked"));
            console.log(`Fulfilled: ${value}`);
        } catch (error) {
            console.log(`Rejected: ${error}`);
        }
    },

    async function usageInAnAsyncFunction3() {
        console.log("Showing rejection in async function");
        try {
            const value = await MyPromise.resolveWithTimeout(100, rejectAfterDelay(50, new Error("failed")));
            console.log(`Fulfilled: ${value}`);
        } catch (error) {
            console.log(`Rejected: ${error}`);
        }
    },
];

(async () => {
    for (const example of examples) {
        try {
            await example();
        } catch (e) {
        }
    }
})();
/* Shows the cosole full height in the snippet */
.as-console-wrapper {
    max-height: 100% !important;
}


上述代码在解决或拒绝promise 时主动取消计时器.这可能不是必需的,这取决于您的用例,并使代码有点复杂.这不是promise 的必要部分;一旦promise 被解决或拒绝,且无法更改,再次调用resolvereject函数对promise 没有影响(the spec对此很清楚).但如果不取消计时器,计时器仍处于挂起状态,直到它触发.一个悬而未决的promise 将保留 node .例如,如果你在做最后一件事时有很长的超时时间,就会毫无意义地延迟退出进程.浏览器不会延迟使用挂起的计时器离开页面,因此这不适用于浏览器.因此,你的里程可能会有所不同,你可以通过不取消计时器来简化一些.

如果您不关心挂起的计时器,MyPromise将是更简单的:

class MyPromise extends Promise {
    constructor(timeout, callback) {
        // We need to support being called with no milliseconds
        // value, because the various Promise methods (`then` and
        // such) correctly call the subclass constructor when
        // building the new promises they return.
        const haveTimeout = typeof timeout === "number";
        const init = haveTimeout ? callback : timeout;
        super((resolve, reject) => {
            init(resolve, reject);
            if (haveTimeout) {
                setTimeout(() => {
                    reject(new Error(`Promise timed out after ${timeout}ms`));
                }, timeout);
            }
        });
    }
    // Pick your own name of course. (You could even override `resolve` itself
    // if you liked; just be sure to do the same arguments detection we do
    // above in the constructor, since you need to support the standard use of
    // `resolve`.)
    static resolveWithTimeout(timeout, x) {
        if (!x || typeof x.then !== "function") {
            // `x` isn't a thenable, no need for the timeout,
            // fulfill immediately
            return this.resolve(x);
        }
        return new this(timeout, x.then.bind(x));
    }
}

Node.js相关问答推荐

AWS Lambda呼叫未登录控制台

从目录中获取所有文件,而不是NodeJS中的单个文件

(0,core_1.default)不是使用@middy/core的lambda处理程序上的函数

无法使用Sequelize连接AWS RDS

在对象的嵌套数组中使用$lookup,创建多个记录作为响应,mongodb

在生产环境中,Nest实例启动时抛出不完整的导入错误

如何使用对象中的常量值验证字符串字段?

找不到react 模块:错误:默认条件应该是最后一个

为什么 client.on("messageCreate") 的 TextChannel 中缺少 nsfw 属性?

module.exports=require('other') 和临时变量有什么区别?

BrowserRouter 无法渲染组件

如何使用 node 在 koa.js 中发送响应

如何让 vscode 假设一个 node.js 上下文,以便它不假设 `fetch` 存在?

Mongoose,如何一次更新多个?

Azure Function 在 2022 年 4 月 26 日禁用将用户字段设置为 HTTP 请求对象

node.js 中 pdfkit-tables 中的垂直线

如何在 Nest.js 中使用查询参数?

构建 vue cli 后如何运行生产站点

如何使用 Node.js 将 base64 编码图像(字符串)直接上传到 Google Cloud Storage 存储桶?

如何解决 Socket.io 404(未找到)错误?